diff --git a/b2g/app/b2g.js b/b2g/app/b2g.js index 0fe4804e343d..2dafce40519d 100644 --- a/b2g/app/b2g.js +++ b/b2g/app/b2g.js @@ -750,3 +750,8 @@ pref("dom.promise.enabled", false); // (only applies when marionette is disabled) // 0 disables the timer. pref("b2g.adb.timeout-hours", 12); + +// enable Skia/GL (OpenGL-accelerated 2D drawing) for large enough 2d canvases, +// falling back to Skia/software for smaller canvases +pref("gfx.canvas.azure.backends", "skia"); +pref("gfx.canvas.azure.accelerated", true); diff --git a/b2g/components/Keyboard.jsm b/b2g/components/Keyboard.jsm index bcd951b34953..021c6125f584 100644 --- a/b2g/components/Keyboard.jsm +++ b/b2g/components/Keyboard.jsm @@ -38,6 +38,12 @@ let Keyboard = { this._messageManager = mm; }, + sendAsyncMessage: function(name, data) { + try { + this.messageManager.sendAsyncMessage(name, data); + } catch(e) { } + }, + init: function keyboardInit() { Services.obs.addObserver(this, 'in-process-browser-or-app-frame-shown', false); Services.obs.addObserver(this, 'remote-browser-frame-shown', false); @@ -186,28 +192,27 @@ let Keyboard = { }, setSelectedOption: function keyboardSetSelectedOption(msg) { - this.messageManager.sendAsyncMessage('Forms:Select:Choice', msg.data); + this.sendAsyncMessage('Forms:Select:Choice', msg.data); }, setSelectedOptions: function keyboardSetSelectedOptions(msg) { - this.messageManager.sendAsyncMessage('Forms:Select:Choice', msg.data); + this.sendAsyncMessage('Forms:Select:Choice', msg.data); }, setSelectionRange: function keyboardSetSelectionRange(msg) { - this.messageManager.sendAsyncMessage('Forms:SetSelectionRange', msg.data); + this.sendAsyncMessage('Forms:SetSelectionRange', msg.data); }, setValue: function keyboardSetValue(msg) { - this.messageManager.sendAsyncMessage('Forms:Input:Value', msg.data); + this.sendAsyncMessage('Forms:Input:Value', msg.data); }, removeFocus: function keyboardRemoveFocus() { - this.messageManager.sendAsyncMessage('Forms:Select:Blur', {}); + this.sendAsyncMessage('Forms:Select:Blur', {}); }, replaceSurroundingText: function keyboardReplaceSurroundingText(msg) { - this.messageManager.sendAsyncMessage('Forms:ReplaceSurroundingText', - msg.data); + this.sendAsyncMessage('Forms:ReplaceSurroundingText', msg.data); }, showInputMethodPicker: function keyboardShowInputMethodPicker() { @@ -225,23 +230,23 @@ let Keyboard = { }, getText: function keyboardGetText(msg) { - this.messageManager.sendAsyncMessage('Forms:GetText', msg.data); + this.sendAsyncMessage('Forms:GetText', msg.data); }, sendKey: function keyboardSendKey(msg) { - this.messageManager.sendAsyncMessage('Forms:Input:SendKey', msg.data); + this.sendAsyncMessage('Forms:Input:SendKey', msg.data); }, getContext: function keyboardGetContext(msg) { - this.messageManager.sendAsyncMessage('Forms:GetContext', msg.data); + this.sendAsyncMessage('Forms:GetContext', msg.data); }, setComposition: function keyboardSetComposition(msg) { - this.messageManager.sendAsyncMessage('Forms:SetComposition', msg.data); + this.sendAsyncMessage('Forms:SetComposition', msg.data); }, endComposition: function keyboardEndComposition(msg) { - this.messageManager.sendAsyncMessage('Forms:EndComposition', msg.data); + this.sendAsyncMessage('Forms:EndComposition', msg.data); } }; diff --git a/b2g/config/gaia.json b/b2g/config/gaia.json index 0159ca4e5ee1..f565f5ad43cf 100644 --- a/b2g/config/gaia.json +++ b/b2g/config/gaia.json @@ -1,4 +1,4 @@ { - "revision": "3a11e24a45501f1fcad106ef588fcc7262722644", + "revision": "a04ef5d325988b36c3fb088d160c389a1e8682e5", "repo_path": "/integration/gaia-central" } diff --git a/b2g/installer/package-manifest.in b/b2g/installer/package-manifest.in index e0e660cdbc58..2553cf7659ae 100644 --- a/b2g/installer/package-manifest.in +++ b/b2g/installer/package-manifest.in @@ -164,6 +164,7 @@ @BINPATH@/components/dom_base.xpt @BINPATH@/components/dom_system.xpt #ifdef MOZ_B2G_RIL +@BINPATH@/components/dom_telephony.xpt @BINPATH@/components/dom_voicemail.xpt @BINPATH@/components/dom_wifi.xpt @BINPATH@/components/dom_system_gonk.xpt @@ -202,7 +203,6 @@ @BINPATH@/components/dom_mobilemessage.xpt @BINPATH@/components/dom_storage.xpt @BINPATH@/components/dom_stylesheets.xpt -@BINPATH@/components/dom_telephony.xpt @BINPATH@/components/dom_threads.xpt @BINPATH@/components/dom_traversal.xpt @BINPATH@/components/dom_views.xpt @@ -460,8 +460,6 @@ @BINPATH@/components/RadioInterfaceLayer.js @BINPATH@/components/MmsService.manifest @BINPATH@/components/MmsService.js -@BINPATH@/components/SmsService.manifest -@BINPATH@/components/SmsService.js @BINPATH@/components/RILContentHelper.js @BINPATH@/components/MobileMessageDatabaseService.manifest @BINPATH@/components/MobileMessageDatabaseService.js @@ -473,8 +471,6 @@ @BINPATH@/components/NetworkStatsManager.manifest @BINPATH@/components/NetworkInterfaceListService.manifest @BINPATH@/components/NetworkInterfaceListService.js -@BINPATH@/components/TelephonyProvider.manifest -@BINPATH@/components/TelephonyProvider.js #endif #ifdef MOZ_ENABLE_DBUS @BINPATH@/components/@DLL_PREFIX@dbusservice@DLL_SUFFIX@ diff --git a/browser/installer/package-manifest.in b/browser/installer/package-manifest.in index 71b851364de3..7842047d3bc9 100644 --- a/browser/installer/package-manifest.in +++ b/browser/installer/package-manifest.in @@ -176,6 +176,7 @@ @BINPATH@/components/dom_base.xpt @BINPATH@/components/dom_system.xpt #ifdef MOZ_B2G_RIL +@BINPATH@/components/dom_telephony.xpt @BINPATH@/components/dom_voicemail.xpt @BINPATH@/components/dom_wifi.xpt @BINPATH@/components/dom_system_gonk.xpt @@ -211,7 +212,6 @@ @BINPATH@/components/dom_mobilemessage.xpt @BINPATH@/components/dom_storage.xpt @BINPATH@/components/dom_stylesheets.xpt -@BINPATH@/components/dom_telephony.xpt @BINPATH@/components/dom_traversal.xpt #ifdef MOZ_WEBSPEECH @BINPATH@/components/dom_webspeechrecognition.xpt @@ -446,8 +446,6 @@ @BINPATH@/components/RadioInterfaceLayer.js @BINPATH@/components/MmsService.manifest @BINPATH@/components/MmsService.js -@BINPATH@/components/SmsService.manifest -@BINPATH@/components/SmsService.js @BINPATH@/components/RILContentHelper.js @BINPATH@/components/MobileMessageDatabaseService.manifest @BINPATH@/components/MobileMessageDatabaseService.js diff --git a/browser/metro/base/content/apzc.js b/browser/metro/base/content/apzc.js index 0509c2950ce3..444edc263005 100644 --- a/browser/metro/base/content/apzc.js +++ b/browser/metro/base/content/apzc.js @@ -15,6 +15,7 @@ let Cr = Components.results; */ var APZCObserver = { + _debugEvents: false, init: function() { this._enabled = Services.prefs.getBoolPref(kAsyncPanZoomEnabled); if (!this._enabled) { @@ -95,25 +96,23 @@ var APZCObserver = { id: scrollId }); - Util.dumpLn("APZC scrollId: " + scrollId); - Util.dumpLn("APZC scrollTo.x: " + scrollTo.x + ", scrollTo.y: " + scrollTo.y); - Util.dumpLn("APZC setResolution: " + resolution); - Util.dumpLn("APZC setDisplayPortForElement: displayPort.x: " + - displayPort.x + ", displayPort.y: " + displayPort.y + - ", displayPort.width: " + displayPort.width + - ", displayort.height: " + displayPort.height); + if (this._debugEvents) { + Util.dumpLn("APZC scrollId: " + scrollId); + Util.dumpLn("APZC scrollTo.x: " + scrollTo.x + ", scrollTo.y: " + scrollTo.y); + Util.dumpLn("APZC setResolution: " + resolution); + Util.dumpLn("APZC setDisplayPortForElement: displayPort.x: " + + displayPort.x + ", displayPort.y: " + displayPort.y + + ", displayPort.width: " + displayPort.width + + ", displayort.height: " + displayPort.height); + } } else if (aTopic == "apzc-handle-pan-begin") { // When we're panning, hide the main scrollbars by setting imprecise // input (which sets a property on the browser which hides the scrollbar // via CSS). This reduces jittering from left to right. We may be able // to get rid of this once we implement axis locking in /gfx APZC. - Util.dumpLn("APZC pan-begin"); if (InputSourceHelper.isPrecise) { InputSourceHelper._imprecise(); } - - } else if (aTopic == "apzc-handle-pan-end") { - Util.dumpLn("APZC pan-end"); } }, diff --git a/browser/metro/profile/metro.js b/browser/metro/profile/metro.js index 5949bf3a8670..3253f9528874 100644 --- a/browser/metro/profile/metro.js +++ b/browser/metro/profile/metro.js @@ -34,7 +34,7 @@ pref("prompts.tab_modal.enabled", true); // Enable off main thread compositing pref("layers.offmainthreadcomposition.enabled", true); -pref("layers.async-pan-zoom.enabled", false); +pref("layers.async-pan-zoom.enabled", true); pref("layers.componentalpha.enabled", false); pref("gfx.azpc.touch_start_tolerance", "0.1"); // dpi * tolerance = pixel threshold pref("gfx.axis.fling_friction", "0.002"); diff --git a/content/canvas/src/CanvasRenderingContext2D.cpp b/content/canvas/src/CanvasRenderingContext2D.cpp index d681a9da69f3..2e0bae558a18 100644 --- a/content/canvas/src/CanvasRenderingContext2D.cpp +++ b/content/canvas/src/CanvasRenderingContext2D.cpp @@ -819,11 +819,10 @@ CanvasRenderingContext2D::RemoveDemotableContext(CanvasRenderingContext2D* conte DemotableContexts().erase(iter); } -#define MIN_SKIA_GL_DIMENSION 16 - bool CheckSizeForSkiaGL(IntSize size) { - return size.width > MIN_SKIA_GL_DIMENSION && size.height > MIN_SKIA_GL_DIMENSION; + int minsize = Preferences::GetInt("gfx.canvas.min-size-for-skia-gl", 128); + return size.width >= minsize && size.height >= minsize; } #endif diff --git a/content/canvas/test/reftest/reftest.list b/content/canvas/test/reftest/reftest.list index c6e7bd9f4afe..fa659e354ddc 100644 --- a/content/canvas/test/reftest/reftest.list +++ b/content/canvas/test/reftest/reftest.list @@ -143,7 +143,7 @@ fuzzy-if(B2G,256,83) random-if(Android&&AndroidVersion<15) == webgl-color-alpha random-if(Android&&AndroidVersion<15) == webgl-color-alpha-test.html?colorVal=0.0&alphaVal=1.0 black.html fuzzy-if(B2G,256,83) random-if(Android&&AndroidVersion<15) == webgl-color-alpha-test.html?colorVal=1.0&alphaVal=0.0 wrapper.html?colors.png -fails-if(B2G) random-if(Android&&AndroidVersion<15) == webgl-color-alpha-test.html?colorVal=1.0&alphaVal=0.0&alpha wrapper.html?white.png +random-if(Android&&AndroidVersion<15) == webgl-color-alpha-test.html?colorVal=1.0&alphaVal=0.0&alpha wrapper.html?white.png fuzzy(1,65536) fuzzy-if(B2G,256,83) fuzzy-if(Android||B2G,9,65536) random-if(Android&&AndroidVersion<15) == webgl-color-alpha-test.html?colorVal=0.5&alphaVal=1.0 wrapper.html?half-colors.png diff --git a/content/media/webrtc/MediaEngineWebRTC.h b/content/media/webrtc/MediaEngineWebRTC.h index 6594e3fcf1c2..e1d6e5ed5286 100644 --- a/content/media/webrtc/MediaEngineWebRTC.h +++ b/content/media/webrtc/MediaEngineWebRTC.h @@ -247,9 +247,8 @@ private: bool mInSnapshotMode; nsString* mSnapshotPath; - // These are in UTF-8 but webrtc api uses char arrays - char mDeviceName[KMaxDeviceNameLength]; - char mUniqueId[KMaxUniqueIdLength]; + nsString mDeviceName; + nsString mUniqueId; void ChooseCapability(const MediaEnginePrefs &aPrefs); }; diff --git a/content/media/webrtc/MediaEngineWebRTCVideo.cpp b/content/media/webrtc/MediaEngineWebRTCVideo.cpp index 55bb061cd565..5b6f417106ea 100644 --- a/content/media/webrtc/MediaEngineWebRTCVideo.cpp +++ b/content/media/webrtc/MediaEngineWebRTCVideo.cpp @@ -6,6 +6,7 @@ #include "Layers.h" #include "ImageTypes.h" #include "ImageContainer.h" +#include "nsMemory.h" #include "mtransport/runnable_utils.h" namespace mozilla { @@ -160,7 +161,8 @@ MediaEngineWebRTCVideoSource::ChooseCapability(const MediaEnginePrefs &aPrefs) mCapability.width = aPrefs.mWidth; mCapability.height = aPrefs.mHeight; #else - int num = mViECapture->NumberOfCapabilities(mUniqueId, KMaxUniqueIdLength); + int num = mViECapture->NumberOfCapabilities(NS_ConvertUTF16toUTF8(mUniqueId).get(), + KMaxUniqueIdLength); LOG(("ChooseCapability: prefs: %dx%d @%d-%dfps", aPrefs.mWidth, aPrefs.mHeight, aPrefs.mFPS, aPrefs.mMinFPS)); @@ -180,7 +182,8 @@ MediaEngineWebRTCVideoSource::ChooseCapability(const MediaEnginePrefs &aPrefs) webrtc::CaptureCapability cap; bool higher = true; for (int i = 0; i < num; i++) { - mViECapture->GetCaptureCapability(mUniqueId, KMaxUniqueIdLength, i, cap); + mViECapture->GetCaptureCapability(NS_ConvertUTF16toUTF8(mUniqueId).get(), + KMaxUniqueIdLength, i, cap); if (higher) { if (i == 0 || (mCapability.width > cap.width && mCapability.height > cap.height)) { @@ -209,15 +212,13 @@ MediaEngineWebRTCVideoSource::ChooseCapability(const MediaEnginePrefs &aPrefs) void MediaEngineWebRTCVideoSource::GetName(nsAString& aName) { - // mDeviceName is UTF8 - CopyUTF8toUTF16(mDeviceName, aName); + aName = mDeviceName; } void MediaEngineWebRTCVideoSource::GetUUID(nsAString& aUUID) { - // mUniqueId is UTF8 - CopyUTF8toUTF16(mUniqueId, aUUID); + aUUID = mUniqueId; } nsresult @@ -242,7 +243,8 @@ MediaEngineWebRTCVideoSource::Allocate(const MediaEnginePrefs &aPrefs) ChooseCapability(aPrefs); - if (mViECapture->AllocateCaptureDevice(mUniqueId, KMaxUniqueIdLength, mCaptureIndex)) { + if (mViECapture->AllocateCaptureDevice(NS_ConvertUTF16toUTF8(mUniqueId).get(), + KMaxUniqueIdLength, mCaptureIndex)) { return NS_ERROR_FAILURE; } mState = kAllocated; @@ -527,18 +529,11 @@ MediaEngineWebRTCVideoSource::Snapshot(uint32_t aDuration, nsIDOMFile** aFile) void MediaEngineWebRTCVideoSource::Init() { - mDeviceName[0] = '\0'; // paranoia - mUniqueId[0] = '\0'; #ifdef MOZ_B2G_CAMERA - nsCString deviceName; + nsAutoCString deviceName; mCameraManager->GetCameraName(mCaptureIndex, deviceName); - - nsString deviceNameUTF16; - deviceNameUTF16.AssignASCII(deviceName.get()); - char* UTF8Name = ToNewUTF8String(deviceNameUTF16); - memcpy(mDeviceName, UTF8Name, strlen(UTF8Name)); - memcpy(mUniqueId, UTF8Name, strlen(UTF8Name)); - NS_Free(UTF8Name); + CopyUTF8toUTF16(deviceName, mDeviceName); + CopyUTF8toUTF16(deviceName, mUniqueId); #else // fix compile warning for these being unused. (remove once used) (void) mFps; @@ -562,11 +557,18 @@ MediaEngineWebRTCVideoSource::Init() return; } + const uint32_t KMaxDeviceNameLength = 128; + const uint32_t KMaxUniqueIdLength = 256; + char deviceName[KMaxDeviceNameLength]; + char uniqueId[KMaxUniqueIdLength]; if (mViECapture->GetCaptureDevice(mCaptureIndex, - mDeviceName, sizeof(mDeviceName), - mUniqueId, sizeof(mUniqueId))) { + deviceName, KMaxDeviceNameLength, + uniqueId, KMaxUniqueIdLength)) { return; } + + CopyUTF8toUTF16(deviceName, mDeviceName); + CopyUTF8toUTF16(uniqueId, mUniqueId); #endif mInitDone = true; diff --git a/dom/apps/src/Webapps.jsm b/dom/apps/src/Webapps.jsm index eeca9499d7c0..0f5a8382f5aa 100644 --- a/dom/apps/src/Webapps.jsm +++ b/dom/apps/src/Webapps.jsm @@ -993,6 +993,7 @@ this.DOMApplicationRegistry = { this.launch( aData.manifestURL, aData.startPoint, + aData.timestamp, function onsuccess() { aMm.sendAsyncMessage("Webapps:Launch:Return:OK", aData); }, @@ -1002,7 +1003,7 @@ this.DOMApplicationRegistry = { ); }, - launch: function launch(aManifestURL, aStartPoint, aOnSuccess, aOnFailure) { + launch: function launch(aManifestURL, aStartPoint, aTimeStamp, aOnSuccess, aOnFailure) { let app = this.getAppByManifestURL(aManifestURL); if (!app) { aOnFailure("NO_SUCH_APP"); @@ -1020,6 +1021,7 @@ this.DOMApplicationRegistry = { // stringified as an empty object. (see bug 830376) let appClone = AppsUtils.cloneAppObject(app); appClone.startPoint = aStartPoint; + appClone.timestamp = aTimeStamp; Services.obs.notifyObservers(null, "webapps-launch", JSON.stringify(appClone)); aOnSuccess(); }, @@ -1947,10 +1949,15 @@ this.DOMApplicationRegistry = { tmpDir.remove(true); } catch(e) { } - // Save the manifest + // Save the manifest and clear the manifest cache, since it may contain + // the update manifest. let manFile = dir.clone(); manFile.append("manifest.webapp"); this._writeFile(manFile, JSON.stringify(aManifest), function() { }); + if (this._manifestCache[aId]) { + delete this._manifestCache[aId]; + } + // Set state and fire events. app.installState = "installed"; app.downloading = false; diff --git a/dom/base/Navigator.cpp b/dom/base/Navigator.cpp index afca9c5111ee..45ba553271be 100644 --- a/dom/base/Navigator.cpp +++ b/dom/base/Navigator.cpp @@ -54,7 +54,7 @@ #include "MediaManager.h" #endif #ifdef MOZ_B2G_RIL -#include "mozilla/dom/telephony/Telephony.h" +#include "Telephony.h" #endif #ifdef MOZ_B2G_BT #include "BluetoothManager.h" diff --git a/dom/bindings/Bindings.conf b/dom/bindings/Bindings.conf index d1e75c49fba1..221886370eae 100644 --- a/dom/bindings/Bindings.conf +++ b/dom/bindings/Bindings.conf @@ -168,12 +168,12 @@ DOMInterfaces = { 'CallEvent': { 'nativeType': 'mozilla::dom::telephony::CallEvent', - 'headerFile': 'mozilla/dom/telephony/CallEvent.h', + 'headerFile': 'CallEvent.h', }, 'CallsList': { 'nativeType': 'mozilla::dom::telephony::CallsList', - 'headerFile': 'mozilla/dom/telephony/CallsList.h', + 'headerFile': 'CallsList.h', }, 'CameraControl': { @@ -1162,17 +1162,17 @@ DOMInterfaces = { 'Telephony' : { 'nativeType': 'mozilla::dom::telephony::Telephony', - 'headerFile': 'mozilla/dom/telephony/Telephony.h', + 'headerFile': 'Telephony.h', }, 'TelephonyCall' : { 'nativeType': 'mozilla::dom::telephony::TelephonyCall', - 'headerFile': 'mozilla/dom/telephony/TelephonyCall.h', + 'headerFile': 'TelephonyCall.h', }, 'TelephonyCallGroup' : { 'nativeType': 'mozilla::dom::telephony::TelephonyCallGroup', - 'headerFile': 'mozilla/dom/telephony/TelephonyCallGroup.h', + 'headerFile': 'TelephonyCallGroup.h', }, 'Text': { diff --git a/dom/bluetooth/BluetoothHfpManager.cpp b/dom/bluetooth/BluetoothHfpManager.cpp index c077d82be160..5ec83c2074d3 100644 --- a/dom/bluetooth/BluetoothHfpManager.cpp +++ b/dom/bluetooth/BluetoothHfpManager.cpp @@ -1483,7 +1483,7 @@ BluetoothHfpManager::OnSocketConnectSuccess(BluetoothSocket* aSocket) } nsCOMPtr provider = - do_GetService(TELEPHONY_PROVIDER_CONTRACTID); + do_GetService(NS_RILCONTENTHELPER_CONTRACTID); NS_ENSURE_TRUE_VOID(provider); provider->EnumerateCalls(mListener->GetListener()); diff --git a/dom/bluetooth/BluetoothTelephonyListener.cpp b/dom/bluetooth/BluetoothTelephonyListener.cpp index 93bfc84e676f..eac3a431ba06 100644 --- a/dom/bluetooth/BluetoothTelephonyListener.cpp +++ b/dom/bluetooth/BluetoothTelephonyListener.cpp @@ -61,11 +61,13 @@ TelephonyListener::EnumerateCallState(uint32_t aCallIndex, bool aIsActive, bool aIsOutgoing, bool aIsEmergency, - bool aIsConference) + bool aIsConference, + bool* aResult) { BluetoothHfpManager* hfp = BluetoothHfpManager::Get(); hfp->HandleCallStateChanged(aCallIndex, aCallState, EmptyString(), aNumber, aIsOutgoing, false); + *aResult = true; return NS_OK; } @@ -115,10 +117,10 @@ bool BluetoothTelephonyListener::StartListening() { nsCOMPtr provider = - do_GetService(TELEPHONY_PROVIDER_CONTRACTID); + do_GetService(NS_RILCONTENTHELPER_CONTRACTID); NS_ENSURE_TRUE(provider, false); - nsresult rv = provider->RegisterListener(mTelephonyListener); + nsresult rv = provider->RegisterTelephonyMsg(mTelephonyListener); NS_ENSURE_SUCCESS(rv, false); return true; @@ -128,10 +130,10 @@ bool BluetoothTelephonyListener::StopListening() { nsCOMPtr provider = - do_GetService(TELEPHONY_PROVIDER_CONTRACTID); + do_GetService(NS_RILCONTENTHELPER_CONTRACTID); NS_ENSURE_TRUE(provider, false); - nsresult rv = provider->UnregisterListener(mTelephonyListener); + nsresult rv = provider->UnregisterTelephonyMsg(mTelephonyListener); return NS_FAILED(rv) ? false : true; } diff --git a/dom/browser-element/BrowserElementChildPreload.js b/dom/browser-element/BrowserElementChildPreload.js index ed95f9c58fea..950f2053adc9 100644 --- a/dom/browser-element/BrowserElementChildPreload.js +++ b/dom/browser-element/BrowserElementChildPreload.js @@ -710,6 +710,13 @@ BrowserElementChild.prototype = { ", maxHeight=" + maxHeight + ", domRequestID=" + domRequestID + "."); + if (!content) { + // If content is not loaded yet, bail out since even sendAsyncMessage + // fails... + debug("No content yet!"); + return; + } + let scaleWidth = Math.min(1, maxWidth / content.innerWidth); let scaleHeight = Math.min(1, maxHeight / content.innerHeight); diff --git a/dom/dom-config.mk b/dom/dom-config.mk index 6450d3c92a03..b001fc9d3789 100644 --- a/dom/dom-config.mk +++ b/dom/dom-config.mk @@ -40,6 +40,7 @@ DOM_SRCDIRS = \ ifdef MOZ_B2G_RIL DOM_SRCDIRS += \ dom/system/gonk \ + dom/telephony \ dom/wifi \ dom/icc/src \ $(NULL) diff --git a/dom/ipc/ContentChild.cpp b/dom/ipc/ContentChild.cpp index 7ebea456bd85..fa5d8ccde545 100644 --- a/dom/ipc/ContentChild.cpp +++ b/dom/ipc/ContentChild.cpp @@ -96,7 +96,6 @@ #include "mozilla/dom/indexedDB/PIndexedDBChild.h" #include "mozilla/dom/mobilemessage/SmsChild.h" -#include "mozilla/dom/telephony/TelephonyChild.h" #include "mozilla/dom/devicestorage/DeviceStorageRequestChild.h" #include "mozilla/dom/bluetooth/PBluetoothChild.h" #include "mozilla/dom/PFMRadioChild.h" @@ -127,7 +126,6 @@ using namespace mozilla::dom::devicestorage; using namespace mozilla::dom::ipc; using namespace mozilla::dom::mobilemessage; using namespace mozilla::dom::indexedDB; -using namespace mozilla::dom::telephony; using namespace mozilla::hal_sandbox; using namespace mozilla::ipc; using namespace mozilla::layers; @@ -921,19 +919,6 @@ ContentChild::DeallocPSmsChild(PSmsChild* aSms) return true; } -PTelephonyChild* -ContentChild::AllocPTelephonyChild() -{ - MOZ_CRASH("No one should be allocating PTelephonyChild actors"); -} - -bool -ContentChild::DeallocPTelephonyChild(PTelephonyChild* aActor) -{ - delete aActor; - return true; -} - PStorageChild* ContentChild::AllocPStorageChild() { diff --git a/dom/ipc/ContentChild.h b/dom/ipc/ContentChild.h index 4dd4d77e20b0..279af64fb619 100644 --- a/dom/ipc/ContentChild.h +++ b/dom/ipc/ContentChild.h @@ -152,9 +152,6 @@ public: virtual PSmsChild* AllocPSmsChild(); virtual bool DeallocPSmsChild(PSmsChild*); - virtual PTelephonyChild* AllocPTelephonyChild(); - virtual bool DeallocPTelephonyChild(PTelephonyChild*); - virtual PStorageChild* AllocPStorageChild(); virtual bool DeallocPStorageChild(PStorageChild* aActor); diff --git a/dom/ipc/ContentParent.cpp b/dom/ipc/ContentParent.cpp index 8f7555931e5d..8a305d4e9eb9 100644 --- a/dom/ipc/ContentParent.cpp +++ b/dom/ipc/ContentParent.cpp @@ -35,7 +35,6 @@ #include "mozilla/dom/PFMRadioParent.h" #include "mozilla/dom/devicestorage/DeviceStorageRequestParent.h" #include "mozilla/dom/GeolocationBinding.h" -#include "mozilla/dom/telephony/TelephonyParent.h" #include "SmsParent.h" #include "mozilla/Hal.h" #include "mozilla/hal_sandbox/PHalParent.h" @@ -149,7 +148,6 @@ using namespace mozilla::dom::devicestorage; using namespace mozilla::dom::indexedDB; using namespace mozilla::dom::power; using namespace mozilla::dom::mobilemessage; -using namespace mozilla::dom::telephony; using namespace mozilla::hal; using namespace mozilla::ipc; using namespace mozilla::layers; @@ -2246,25 +2244,6 @@ ContentParent::DeallocPSmsParent(PSmsParent* aSms) return true; } -PTelephonyParent* -ContentParent::AllocPTelephonyParent() -{ - if (!AssertAppProcessPermission(this, "telephony")) { - return nullptr; - } - - TelephonyParent* actor = new TelephonyParent(); - NS_ADDREF(actor); - return actor; -} - -bool -ContentParent::DeallocPTelephonyParent(PTelephonyParent* aActor) -{ - static_cast(aActor)->Release(); - return true; -} - PStorageParent* ContentParent::AllocPStorageParent() { diff --git a/dom/ipc/ContentParent.h b/dom/ipc/ContentParent.h index 252e0235c90c..5bfa77b18d1a 100644 --- a/dom/ipc/ContentParent.h +++ b/dom/ipc/ContentParent.h @@ -321,9 +321,6 @@ private: virtual PSmsParent* AllocPSmsParent(); virtual bool DeallocPSmsParent(PSmsParent*); - virtual PTelephonyParent* AllocPTelephonyParent(); - virtual bool DeallocPTelephonyParent(PTelephonyParent*); - virtual PStorageParent* AllocPStorageParent(); virtual bool DeallocPStorageParent(PStorageParent* aActor); diff --git a/dom/ipc/PContent.ipdl b/dom/ipc/PContent.ipdl index a4f5e80d1e1b..eb706f624d47 100644 --- a/dom/ipc/PContent.ipdl +++ b/dom/ipc/PContent.ipdl @@ -20,7 +20,6 @@ include protocol PNecko; include protocol PSms; include protocol PSpeechSynthesis; include protocol PStorage; -include protocol PTelephony; include protocol PTestShell; include protocol PJavaScript; include DOMTypes; @@ -198,7 +197,6 @@ rpc protocol PContent manages PSms; manages PSpeechSynthesis; manages PStorage; - manages PTelephony; manages PTestShell; manages PJavaScript; @@ -355,8 +353,6 @@ parent: PStorage(); - PTelephony(); - PBluetooth(); PFMRadio(); diff --git a/dom/mobilemessage/interfaces/moz.build b/dom/mobilemessage/interfaces/moz.build index dd41f43b7761..52cc79b9288c 100644 --- a/dom/mobilemessage/interfaces/moz.build +++ b/dom/mobilemessage/interfaces/moz.build @@ -25,7 +25,6 @@ XPIDL_SOURCES += [ if CONFIG['MOZ_B2G_RIL']: XPIDL_SOURCES += [ 'nsIRilMobileMessageDatabaseService.idl', - 'nsIRilSmsService.idl', ] XPIDL_MODULE = 'dom_mobilemessage' diff --git a/dom/mobilemessage/interfaces/nsIRilSmsService.idl b/dom/mobilemessage/interfaces/nsIRilSmsService.idl deleted file mode 100644 index ef2e853d8a5e..000000000000 --- a/dom/mobilemessage/interfaces/nsIRilSmsService.idl +++ /dev/null @@ -1,11 +0,0 @@ -/* 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 "nsISmsService.idl" - -[scriptable, uuid(f216903c-bdf5-4988-b894-f62fd91df114)] -interface nsIRilSmsService : nsISmsService -{ - void notifyMessageReceived(in jsval message); -}; diff --git a/dom/mobilemessage/interfaces/nsISmsService.idl b/dom/mobilemessage/interfaces/nsISmsService.idl index a6bcebea02d0..e3cde07aeb31 100644 --- a/dom/mobilemessage/interfaces/nsISmsService.idl +++ b/dom/mobilemessage/interfaces/nsISmsService.idl @@ -13,7 +13,7 @@ interface nsIMobileMessageCallback; #define SMS_SERVICE_CONTRACTID "@mozilla.org/sms/smsservice;1" %} -[scriptable, uuid(7ef8e361-9db6-46ed-badc-2901e1049e5d)] +[scriptable, builtinclass, uuid(0f3f75ec-00dd-11e3-87ac-0b1d5c79afdf)] interface nsISmsService : nsISupports { boolean hasSupport(); @@ -26,6 +26,7 @@ interface nsISmsService : nsISupports in boolean silent, in nsIMobileMessageCallback request); + boolean isSilentNumber(in DOMString number); void addSilentNumber(in DOMString number); void removeSilentNumber(in DOMString number); }; diff --git a/dom/mobilemessage/src/Makefile.in b/dom/mobilemessage/src/Makefile.in index 78367d9887ea..f64158868302 100644 --- a/dom/mobilemessage/src/Makefile.in +++ b/dom/mobilemessage/src/Makefile.in @@ -26,5 +26,12 @@ LOCAL_INCLUDES = \ # subdirectory (and the ipc one). LOCAL_INCLUDES += $(VPATH:%=-I%) +ifdef MOZ_B2G_RIL +LOCAL_INCLUDES += \ + -I$(topsrcdir)/dom/telephony \ + -I$(topsrcdir)/dom/system/gonk \ + $(NULL) +endif + include $(topsrcdir)/config/rules.mk include $(topsrcdir)/ipc/chromium/chromium-config.mk diff --git a/dom/mobilemessage/src/SmsServicesFactory.cpp b/dom/mobilemessage/src/SmsServicesFactory.cpp index 94ffef77b929..efcf0171834b 100644 --- a/dom/mobilemessage/src/SmsServicesFactory.cpp +++ b/dom/mobilemessage/src/SmsServicesFactory.cpp @@ -5,18 +5,16 @@ #include "SmsServicesFactory.h" #include "nsXULAppAPI.h" +#include "SmsService.h" #include "SmsIPCService.h" #ifndef MOZ_B2G_RIL #include "MobileMessageDatabaseService.h" #include "MmsService.h" -#include "SmsService.h" #endif #include "nsServiceManagerUtils.h" #define RIL_MMSSERVICE_CONTRACTID "@mozilla.org/mms/rilmmsservice;1" -#define RIL_MOBILE_MESSAGE_DATABASE_SERVICE_CONTRACTID \ - "@mozilla.org/mobilemessage/rilmobilemessagedatabaseservice;1" -#define RIL_SMSSERVICE_CONTRACTID "@mozilla.org/sms/rilsmsservice;1" +#define RIL_MOBILE_MESSAGE_DATABASE_SERVICE_CONTRACTID "@mozilla.org/mobilemessage/rilmobilemessagedatabaseservice;1" namespace mozilla { namespace dom { @@ -30,11 +28,7 @@ SmsServicesFactory::CreateSmsService() if (XRE_GetProcessType() == GeckoProcessType_Content) { smsService = new SmsIPCService(); } else { -#ifdef MOZ_B2G_RIL - smsService = do_GetService(RIL_SMSSERVICE_CONTRACTID); -#else smsService = new SmsService(); -#endif } return smsService.forget(); @@ -48,8 +42,7 @@ SmsServicesFactory::CreateMobileMessageDatabaseService() mobileMessageDBService = new SmsIPCService(); } else { #ifdef MOZ_B2G_RIL - mobileMessageDBService = - do_GetService(RIL_MOBILE_MESSAGE_DATABASE_SERVICE_CONTRACTID); + mobileMessageDBService = do_GetService(RIL_MOBILE_MESSAGE_DATABASE_SERVICE_CONTRACTID); #else mobileMessageDBService = new MobileMessageDatabaseService(); #endif diff --git a/dom/mobilemessage/src/android/SmsService.cpp b/dom/mobilemessage/src/android/SmsService.cpp index 8233e54c8163..1da4b0a7ead8 100644 --- a/dom/mobilemessage/src/android/SmsService.cpp +++ b/dom/mobilemessage/src/android/SmsService.cpp @@ -50,6 +50,14 @@ SmsService::Send(const nsAString& aNumber, return NS_OK; } +NS_IMETHODIMP +SmsService::IsSilentNumber(const nsAString& aNumber, + bool* aIsSilent) +{ + NS_NOTYETIMPLEMENTED("Implement me!"); + return NS_ERROR_NOT_IMPLEMENTED; +} + NS_IMETHODIMP SmsService::AddSilentNumber(const nsAString& aNumber) { diff --git a/dom/mobilemessage/src/fallback/SmsService.cpp b/dom/mobilemessage/src/fallback/SmsService.cpp index bfc32de00160..a04a46765491 100644 --- a/dom/mobilemessage/src/fallback/SmsService.cpp +++ b/dom/mobilemessage/src/fallback/SmsService.cpp @@ -39,6 +39,14 @@ SmsService::Send(const nsAString& aNumber, return NS_ERROR_FAILURE; } +NS_IMETHODIMP +SmsService::IsSilentNumber(const nsAString& aNumber, + bool* aIsSilent) +{ + NS_ERROR("We should not be here!"); + return NS_ERROR_FAILURE; +} + NS_IMETHODIMP SmsService::AddSilentNumber(const nsAString& aNumber) { diff --git a/dom/mobilemessage/src/gonk/SmsService.cpp b/dom/mobilemessage/src/gonk/SmsService.cpp new file mode 100644 index 000000000000..335334786fb4 --- /dev/null +++ b/dom/mobilemessage/src/gonk/SmsService.cpp @@ -0,0 +1,85 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 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/. */ + +#include "SmsMessage.h" +#include "SmsService.h" +#include "jsapi.h" +#include "SmsSegmentInfo.h" + +namespace mozilla { +namespace dom { +namespace mobilemessage { + +NS_IMPL_ISUPPORTS1(SmsService, nsISmsService) + +SmsService::SmsService() +{ + nsCOMPtr ril = do_GetService("@mozilla.org/ril;1"); + if (ril) { + ril->GetRadioInterface(0, getter_AddRefs(mRadioInterface)); + } + NS_WARN_IF_FALSE(mRadioInterface, "This shouldn't fail!"); +} + +NS_IMETHODIMP +SmsService::HasSupport(bool* aHasSupport) +{ + *aHasSupport = true; + return NS_OK; +} + +NS_IMETHODIMP +SmsService::GetSegmentInfoForText(const nsAString& aText, + nsIMobileMessageCallback* aRequest) +{ + NS_ENSURE_TRUE(mRadioInterface, NS_ERROR_FAILURE); + + return mRadioInterface->GetSegmentInfoForText(aText, aRequest); +} + +NS_IMETHODIMP +SmsService::Send(const nsAString& aNumber, + const nsAString& aMessage, + const bool aSilent, + nsIMobileMessageCallback* aRequest) +{ + NS_ENSURE_TRUE(mRadioInterface, NS_ERROR_FAILURE); + + return mRadioInterface->SendSMS(aNumber, aMessage, aSilent, aRequest); +} + +NS_IMETHODIMP +SmsService::IsSilentNumber(const nsAString& aNumber, + bool* aIsSilent) +{ + *aIsSilent = mSilentNumbers.Contains(aNumber); + return NS_OK; +} + +NS_IMETHODIMP +SmsService::AddSilentNumber(const nsAString& aNumber) +{ + if (mSilentNumbers.Contains(aNumber)) { + return NS_ERROR_UNEXPECTED; + } + + NS_ENSURE_TRUE(mSilentNumbers.AppendElement(aNumber), NS_ERROR_FAILURE); + return NS_OK; +} + +NS_IMETHODIMP +SmsService::RemoveSilentNumber(const nsAString& aNumber) +{ + if (!mSilentNumbers.Contains(aNumber)) { + return NS_ERROR_INVALID_ARG; + } + + NS_ENSURE_TRUE(mSilentNumbers.RemoveElement(aNumber), NS_ERROR_FAILURE); + return NS_OK; +} + +} // namespace mobilemessage +} // namespace dom +} // namespace mozilla diff --git a/dom/mobilemessage/src/gonk/SmsService.h b/dom/mobilemessage/src/gonk/SmsService.h new file mode 100644 index 000000000000..940cbf2fa816 --- /dev/null +++ b/dom/mobilemessage/src/gonk/SmsService.h @@ -0,0 +1,35 @@ +/* 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/. */ + +#ifndef mozilla_dom_mobilemessage_SmsService_h +#define mozilla_dom_mobilemessage_SmsService_h + +#include "nsISmsService.h" +#include "nsCOMPtr.h" +#include "nsIRadioInterfaceLayer.h" +#include "nsTArray.h" +#include "nsString.h" + +namespace mozilla { +namespace dom { +namespace mobilemessage { + +class SmsService : public nsISmsService +{ +public: + NS_DECL_ISUPPORTS + NS_DECL_NSISMSSERVICE + SmsService(); + +protected: + // TODO: Bug 854326 - B2G Multi-SIM: support multiple SIM cards for SMS/MMS + nsCOMPtr mRadioInterface; + nsTArray mSilentNumbers; +}; + +} // namespace mobilemessage +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_mobilemessage_SmsService_h diff --git a/dom/mobilemessage/src/gonk/SmsService.js b/dom/mobilemessage/src/gonk/SmsService.js deleted file mode 100644 index 8da097f93378..000000000000 --- a/dom/mobilemessage/src/gonk/SmsService.js +++ /dev/null @@ -1,1015 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- - * vim: sw=2 ts=2 sts=2 et filetype=javascript - * 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/XPCOMUtils.jsm"); -Cu.import("resource://gre/modules/Services.jsm"); - -var RIL = {}; -Cu.import("resource://gre/modules/ril_consts.js", RIL); - -const RIL_SMSSERVICE_CONTRACTID = "@mozilla.org/sms/rilsmsservice;1"; -const RIL_SMSSERVICE_CID = - Components.ID("{46a9ed78-3574-40a1-9f12-ea179942d67f}"); - -const DELIVERY_STATE_RECEIVED = "received"; -const DELIVERY_STATE_SENDING = "sending"; -const DELIVERY_STATE_SENT = "sent"; -const DELIVERY_STATE_ERROR = "error"; - -// Observer topics to send. -const kSmsReceivedObserverTopic = "sms-received"; -const kSmsSendingObserverTopic = "sms-sending"; -const kSmsSentObserverTopic = "sms-sent"; -const kSmsFailedObserverTopic = "sms-failed"; -const kSmsDeliverySuccessObserverTopic = "sms-delivery-success"; -const kSmsDeliveryErrorObserverTopic = "sms-delivery-error"; -const kSilentSmsReceivedObserverTopic = "silent-sms-received"; - -// Observer topics to watch. -const kPrefenceChangedObserverTopic = "nsPref:changed"; -const kXpcomShutdownObserverTopic = "xpcom-shutdown"; - -// Preference keys. -const kPrefKeyRilDebuggingEnabled = "ril.debugging.enabled"; - -// TODO: Bug 815526 - [meta] B2G RIL: deprecate RILContentHelper -XPCOMUtils.defineLazyServiceGetter(this, "gIccProvider", - "@mozilla.org/ril/content-helper;1", - "nsIIccProvider"); - -XPCOMUtils.defineLazyServiceGetter(this, "gMobileMessageDatabaseService", - "@mozilla.org/mobilemessage/rilmobilemessagedatabaseservice;1", - "nsIRilMobileMessageDatabaseService"); - -XPCOMUtils.defineLazyServiceGetter(this, "gMobileMessageService", - "@mozilla.org/mobilemessage/mobilemessageservice;1", - "nsIMobileMessageService"); - -XPCOMUtils.defineLazyServiceGetter(this, "gSystemMessenger", - "@mozilla.org/system-message-internal;1", - "nsISystemMessagesInternal"); - -XPCOMUtils.defineLazyGetter(this, "gRadioInterface", function () { - let ril = Cc["@mozilla.org/ril;1"].getService(Ci.nsIRadioInterfaceLayer); - // TODO: Bug 854326 - B2G Multi-SIM: support multiple SIM cards for SMS/MMS - return ril.getRadioInterface(0); -}); - -XPCOMUtils.defineLazyGetter(this, "gPhoneNumberUtils", function () { - let ns = {}; - Cu.import("resource://gre/modules/PhoneNumberUtils.jsm", ns); - return ns.PhoneNumberUtils; -}); - -XPCOMUtils.defineLazyGetter(this, "WAP", function () { - let WAP = {}; - Cu.import("resource://gre/modules/WapPushManager.js", WAP); - return WAP; -}); - -// TODO: Bug 833229 - B2G RIL: use ipdl as IPC in MozVoicemail -XPCOMUtils.defineLazyGetter(this, "gMessageManager", function () { - let ns = {}; - Cu.import("resource://gre/modules/RilMessageManager.jsm", ns); - return ns.RilMessageManager; -}); - -let DEBUG; -function debug(s) { - dump("SmsService: " + s + "\n"); -} - -/** - * SmsService - */ -function SmsService() { - this._updateDebugFlag(); - - this.silentNumbers = []; - - this.portAddressedSmsApps = {}; - this.portAddressedSmsApps[WAP.WDP_PORT_PUSH] = - this._handleSmsWdpPortPush.bind(this); - - Services.obs.addObserver(this, kPrefenceChangedObserverTopic, false); - Services.obs.addObserver(this, kXpcomShutdownObserverTopic, false); -} -SmsService.prototype = { - - classID: RIL_SMSSERVICE_CID, - classInfo: XPCOMUtils.generateCI({classID: RIL_SMSSERVICE_CID, - contractID: RIL_SMSSERVICE_CONTRACTID, - classDescription: "SmsService", - interfaces: [Ci.nsIRilSmsService, - Ci.nsISmsService], - flags: Ci.nsIClassInfo.SINGLETON}), - QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver, - Ci.nsIRilSmsService, - Ci.nsISmsService]), - - /** - * 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], - ], - - /** - * Use 16-bit reference number for concatenated outgoint messages. - * - * TODO: Support static/runtime settings, see bug 733331. - */ - segmentRef16Bit: false, - - /** - * Get valid SMS concatenation reference number. - */ - segmentRef: 0, - get nextSegmentRef() { - let ref = this.segmentRef++; - - this.segmentRef %= (this.segmentRef16Bit ? 65535 : 255); - - // 0 is not a valid SMS concatenation reference number. - return ref + 1; - }, - - statusReportPendingMessageIds: null, - - portAddressedSmsApps: null, - - silentNumbers: null, - - _updateDebugFlag: function _updateDebugFlag() { - try { - DEBUG = RIL.DEBUG_RIL || - Services.prefs.getBoolPref("ril.debugging.enabled"); - } catch (e) {} - }, - - _getStrict7BitEncoding: function _getStrict7BitEncoding() { - try { - return Services.prefs.getBoolPref("dom.sms.strict7BitEncoding"); - } catch (e) { - return false; - } - }, - - _getRequestStatusReport: function _getRequestStatusReport() { - try { - return Services.prefs.getBoolPref("dom.sms.requestStatusReport"); - } catch (e) { - return true; - } - }, - - _getMsisdn: function _getMsisdn() { - let iccInfo = gIccProvider.iccInfo; - if (!iccInfo) { - return null; - } - - let number; - if (iccInfo.iccType == "ruim") { - let cdmaIccInfo = iccInfo.QueryInterface(Ci.nsIDOMMozCdmaIccInfo); - number = cdmaIccInfo.mdn; - } else { - let gsmIccInfo = iccInfo.QueryInterface(Ci.nsIDOMMozGsmIccInfo); - number = gsmIccInfo.msisdn; - } - - // Workaround an xpconnect issue with undefined string objects. - // See bug 808220 - if (number === undefined || number === "undefined") { - return null; - } - return number; - }, - - /** - * Calculate encoded length using specified locking/single shift table - * - * @param text - * text string to be encoded. - * @param langTable - * locking shift table string. - * @param langShiftTable - * single shift table string. - * @param strict7BitEncoding - * Optional. Enable Latin characters replacement with corresponding - * ones in GSM SMS 7-bit default alphabet. - * - * @return encoded length in septets. - * - * @note that the algorithm used in this function must match exactly with - * GsmPDUHelper#writeStringAsSeptets. - */ - _countGsm7BitSeptets: function _countGsm7BitSeptets(text, - langTable, - langShiftTable, - strict7BitEncoding) { - let length = 0; - for (let msgIndex = 0; msgIndex < text.length; msgIndex++) { - let c = text.charAt(msgIndex); - if (strict7BitEncoding) { - c = RIL.GSM_SMS_STRICT_7BIT_CHARMAP[c] || c; - } - - let septet = langTable.indexOf(c); - - // According to 3GPP TS 23.038, section 6.1.1 General notes, "The - // characters marked '1)' are not used but are displayed as a space." - if (septet == RIL.PDU_NL_EXTENDED_ESCAPE) { - continue; - } - - if (septet >= 0) { - length++; - continue; - } - - 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 = '*'; - 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; - } - - continue; - } - - // According to 3GPP TS 23.038 B.2, "This code represents a control - // character and therefore must not be used for language specific - // characters." - if (septet == RIL.PDU_NL_RESERVED_CONTROL) { - continue; - } - - // The character is not found in locking shfit table, but could be - // encoded as with single shift table. Note that it's - // still possible for septet to has the value of PDU_NL_EXTENDED_ESCAPE, - // but we can display it as a space in this case as said in previous - // comment. - length += 2; - } - - return length; - }, - - /** - * Calculate user data length of specified text string encoded in GSM 7Bit - * alphabets. - * - * @param text - * a text string to be encoded. - * @param strict7BitEncoding - * Optional. Enable Latin characters replacement with corresponding - * ones in GSM SMS 7-bit default alphabet. - * - * @return null or an options object with attributes `dcs`, - * `userDataHeaderLength`, `encodedFullBodyLength`, `langIndex`, - * `langShiftIndex`, `segmentMaxSeq` set. - * - * @see #_calculateUserDataLength(). - */ - _calculateUserDataLength7Bit: function _calculateUserDataLength7Bit(text, - strict7BitEncoding) { - let options = null; - let minUserDataSeptets = Number.MAX_VALUE; - for (let i = 0; i < this.enabledGsmTableTuples.length; i++) { - let [langIndex, langShiftIndex] = this.enabledGsmTableTuples[i]; - - const langTable = RIL.PDU_NL_LOCKING_SHIFT_TABLES[langIndex]; - const langShiftTable = RIL.PDU_NL_SINGLE_SHIFT_TABLES[langShiftIndex]; - - let bodySeptets = this._countGsm7BitSeptets(text, - langTable, - langShiftTable, - strict7BitEncoding); - if (bodySeptets < 0) { - continue; - } - - let headerLen = 0; - if (langIndex != RIL.PDU_NL_IDENTIFIER_DEFAULT) { - headerLen += 3; // IEI + len + langIndex - } - if (langShiftIndex != RIL.PDU_NL_IDENTIFIER_DEFAULT) { - headerLen += 3; // IEI + len + langShiftIndex - } - - // Calculate full user data length, note the extra byte is for header len - let headerSeptets = Math.ceil((headerLen ? headerLen + 1 : 0) * 8 / 7); - let segmentSeptets = RIL.PDU_MAX_USER_DATA_7BIT; - if ((bodySeptets + headerSeptets) > segmentSeptets) { - headerLen += this.segmentRef16Bit ? 6 : 5; - headerSeptets = Math.ceil((headerLen + 1) * 8 / 7); - segmentSeptets -= headerSeptets; - } - - let segments = Math.ceil(bodySeptets / segmentSeptets); - let userDataSeptets = bodySeptets + headerSeptets * segments; - if (userDataSeptets >= minUserDataSeptets) { - continue; - } - - minUserDataSeptets = userDataSeptets; - - options = { - dcs: RIL.PDU_DCS_MSG_CODING_7BITS_ALPHABET, - encodedFullBodyLength: bodySeptets, - userDataHeaderLength: headerLen, - langIndex: langIndex, - langShiftIndex: langShiftIndex, - segmentMaxSeq: segments, - segmentChars: segmentSeptets, - }; - } - - return options; - }, - - /** - * Calculate user data length of specified text string encoded in UCS2. - * - * @param text - * a text string to be encoded. - * - * @return an options object with attributes `dcs`, `userDataHeaderLength`, - * `encodedFullBodyLength`, `segmentMaxSeq` set. - * - * @see #_calculateUserDataLength(). - */ - _calculateUserDataLengthUCS2: function _calculateUserDataLengthUCS2(text) { - let bodyChars = text.length; - let headerLen = 0; - let headerChars = Math.ceil((headerLen ? headerLen + 1 : 0) / 2); - let segmentChars = RIL.PDU_MAX_USER_DATA_UCS2; - if ((bodyChars + headerChars) > segmentChars) { - headerLen += this.segmentRef16Bit ? 6 : 5; - headerChars = Math.ceil((headerLen + 1) / 2); - segmentChars -= headerChars; - } - - let segments = Math.ceil(bodyChars / segmentChars); - - return { - dcs: RIL.PDU_DCS_MSG_CODING_16BITS_ALPHABET, - encodedFullBodyLength: bodyChars * 2, - userDataHeaderLength: headerLen, - segmentMaxSeq: segments, - segmentChars: segmentChars, - }; - }, - - /** - * Calculate user data length and its encoding. - * - * @param text - * a text string to be encoded. - * @param strict7BitEncoding - * Optional. Enable Latin characters replacement with corresponding - * ones in GSM SMS 7-bit default alphabet. - * - * @return an options object with some or all of following attributes set: - * - * @param dcs - * Data coding scheme. One of the PDU_DCS_MSG_CODING_*BITS_ALPHABET - * constants. - * @param userDataHeaderLength - * Length of embedded user data header, in bytes. The whole header - * size will be userDataHeaderLength + 1; 0 for no header. - * @param encodedFullBodyLength - * Length of the text body when encoded with the given DCS. For - * UCS2, in bytes; for 7-bit, in septets. - * @param langIndex - * Table index used for normal 7-bit encoded character lookup. - * @param langShiftIndex - * Table index used for escaped 7-bit encoded character lookup. - * @param segmentMaxSeq - * Max sequence number of a multi-part messages, or 1 for single one. - * This number might not be accurate for a multi-part text until - * it's processed by #_fragmentText() again. - */ - _calculateUserDataLength: function _calculateUserDataLength(text, - strict7BitEncoding) { - let options = this._calculateUserDataLength7Bit(text, strict7BitEncoding); - if (!options) { - options = this._calculateUserDataLengthUCS2(text); - } - - if (DEBUG) debug("_calculateUserDataLength: " + JSON.stringify(options)); - return options; - }, - - /** - * Fragment GSM 7-Bit encodable string for transmission. - * - * @param text - * text string to be fragmented. - * @param langTable - * locking shift table string. - * @param langShiftTable - * single shift table string. - * @param segmentSeptets - * Number of available spetets per segment. - * @param strict7BitEncoding - * Optional. Enable Latin characters replacement with corresponding - * ones in GSM SMS 7-bit default alphabet. - * - * @return an array of objects. See #_fragmentText() for detailed definition. - */ - _fragmentText7Bit: function _fragmentText7Bit(text, langTable, langShiftTable, - segmentSeptets, - strict7BitEncoding) { - let ret = []; - let body = "", len = 0; - for (let i = 0, inc = 0; i < text.length; i++) { - let c = text.charAt(i); - if (strict7BitEncoding) { - c = RIL.GSM_SMS_STRICT_7BIT_CHARMAP[c] || c; - } - - let septet = langTable.indexOf(c); - if (septet == RIL.PDU_NL_EXTENDED_ESCAPE) { - continue; - } - - if (septet >= 0) { - inc = 1; - } else { - septet = langShiftTable.indexOf(c); - if (septet == RIL.PDU_NL_RESERVED_CONTROL) { - continue; - } - - 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 = '*'; - if (langTable.indexOf(c) >= 0) { - inc = 1; - } - } - } - - if ((len + inc) > segmentSeptets) { - ret.push({ - body: body, - encodedBodyLength: len, - }); - body = c; - len = inc; - } else { - body += c; - len += inc; - } - } - - if (len) { - ret.push({ - body: body, - encodedBodyLength: len, - }); - } - - return ret; - }, - - /** - * Fragment UCS2 encodable string for transmission. - * - * @param text - * text string to be fragmented. - * @param segmentChars - * Number of available characters per segment. - * - * @return an array of objects. See #_fragmentText() for detailed definition. - */ - _fragmentTextUCS2: function _fragmentTextUCS2(text, segmentChars) { - let ret = []; - for (let offset = 0; offset < text.length; offset += segmentChars) { - let str = text.substr(offset, segmentChars); - ret.push({ - body: str, - encodedBodyLength: str.length * 2, - }); - } - - return ret; - }, - - /** - * Fragment string for transmission. - * - * Fragment input text string into an array of objects that contains - * attributes `body`, substring for this segment, `encodedBodyLength`, - * length of the encoded segment body in septets. - * - * @param text - * Text string to be fragmented. - * @param options - * Optional pre-calculated option object. The output array will be - * stored at options.segments if there are multiple segments. - * @param strict7BitEncoding - * Optional. Enable Latin characters replacement with corresponding - * ones in GSM SMS 7-bit default alphabet. - * - * @return Populated options object. - */ - _fragmentText: function _fragmentText(text, options, strict7BitEncoding) { - if (!options) { - options = this._calculateUserDataLength(text, strict7BitEncoding); - } - - if (options.dcs == RIL.PDU_DCS_MSG_CODING_7BITS_ALPHABET) { - const langTable = RIL.PDU_NL_LOCKING_SHIFT_TABLES[options.langIndex]; - const langShiftTable = RIL.PDU_NL_SINGLE_SHIFT_TABLES[options.langShiftIndex]; - options.segments = this._fragmentText7Bit(text, - langTable, langShiftTable, - options.segmentChars, - strict7BitEncoding); - } else { - options.segments = this._fragmentTextUCS2(text, - options.segmentChars); - } - - // Re-sync options.segmentMaxSeq with actual length of returning array. - options.segmentMaxSeq = options.segments.length; - - return options; - }, - - /** - * A helper to broadcast the system message to launch registered apps - * like Costcontrol, Notification and Message app... etc. - * - * @param aName - * The system message name. - * @param aDomMessage - * The nsIDOMMozSmsMessage object. - */ - _broadcastSystemMessage: function _broadcastSystemMessage(aName, aDomMessage) { - if (DEBUG) debug("Broadcasting the SMS system message: " + aName); - - // Sadly we cannot directly broadcast the aDomMessage object - // because the system message mechamism will rewrap the object - // based on the content window, which needs to know the properties. - gSystemMessenger.broadcastMessage(aName, { - type: aDomMessage.type, - id: aDomMessage.id, - threadId: aDomMessage.threadId, - delivery: aDomMessage.delivery, - deliveryStatus: aDomMessage.deliveryStatus, - sender: aDomMessage.sender, - receiver: aDomMessage.receiver, - body: aDomMessage.body, - messageClass: aDomMessage.messageClass, - timestamp: aDomMessage.timestamp, - read: aDomMessage.read - }); - }, - - /** - * Handle WDP port push PDU. Constructor WDP bearer information and deliver - * to WapPushManager. - * - * @param message - * A SMS message. - */ - _handleSmsWdpPortPush: function _handleSmsWdpPortPush(message) { - if (message.encoding != RIL.PDU_DCS_MSG_CODING_8BITS_ALPHABET) { - if (DEBUG) { - debug("Got port addressed SMS but not encoded in 8-bit alphabet." + - " Drop!"); - } - return; - } - - let options = { - bearer: WAP.WDP_BEARER_GSM_SMS_GSM_MSISDN, - sourceAddress: message.sender, - sourcePort: message.header.originatorPort, - destinationAddress: this.rilContext.iccInfo.msisdn, - destinationPort: message.header.destinationPort, - }; - WAP.WapPushManager.receiveWdpPDU(message.fullData, message.fullData.length, - 0, options); - }, - - _isSilentNumber: function _isSilentNumber(number) { - return this.silentNumbers.indexOf(number) >= 0; - }, - - /** - * nsISmsService methods. - */ - - // TODO: Bug 859616 - WebSMS: return undefined if the API is unsupported on - // the platform, not null - hasSupport: function hasSupport() { - return true; - }, - - getSegmentInfoForText: function getSegmentInfoForText(text, request) { - let strict7BitEncoding = this._getStrict7BitEncoding(); - - let options = this._fragmentText(text, null, strict7BitEncoding); - let charsInLastSegment; - if (options.segmentMaxSeq) { - let lastSegment = options.segments[options.segmentMaxSeq - 1]; - charsInLastSegment = lastSegment.encodedBodyLength; - if (options.dcs == RIL.PDU_DCS_MSG_CODING_16BITS_ALPHABET) { - // In UCS2 encoding, encodedBodyLength is in octets. - charsInLastSegment /= 2; - } - } else { - charsInLastSegment = 0; - } - - let result = gMobileMessageService - .createSmsSegmentInfo(options.segmentMaxSeq, - options.segmentChars, - options.segmentChars - charsInLastSegment); - request.notifySegmentInfoForTextGot(result); - }, - - send: function send(number, text, silent, request) { - let strict7BitEncoding = this._getStrict7BitEncoding(); - let requestStatusReport = this._getRequestStatusReport(); - - let options = this._fragmentText(text, null, strict7BitEncoding); - options.number = gPhoneNumberUtils.normalize(number); - options.requestStatusReport = requestStatusReport && !silent; - if (options.segmentMaxSeq > 1) { - options.segmentRef16Bit = this.segmentRef16Bit; - options.segmentRef = this.nextSegmentRef; - } - - let notifyResult = (function notifyResult(rv, domMessage) { - // TODO bug 832140 handle !Components.isSuccessCode(rv) - if (!silent) { - Services.obs.notifyObservers(domMessage, kSmsSendingObserverTopic, null); - } - - // If the radio is disabled or the SIM card is not ready, just directly - // return with the corresponding error code. - let errorCode; - if (!gPhoneNumberUtils.isPlainPhoneNumber(options.number)) { - if (DEBUG) debug("Error! Address is invalid when sending SMS: " + - options.number); - errorCode = Ci.nsIMobileMessageCallback.INVALID_ADDRESS_ERROR; - } else if (gRadioInterface.rilContext.radioState != - RIL.GECKO_RADIOSTATE_READY) { - if (DEBUG) debug("Error! Radio is disabled when sending SMS."); - errorCode = Ci.nsIMobileMessageCallback.RADIO_DISABLED_ERROR; - } else if (gRadioInterface.rilContext.cardState != - RIL.GECKO_CARDSTATE_READY) { - if (DEBUG) debug("Error! SIM card is not ready when sending SMS."); - errorCode = Ci.nsIMobileMessageCallback.NO_SIM_CARD_ERROR; - } - if (errorCode) { - if (silent) { - request.notifySendMessageFailed(errorCode); - return; - } - - gMobileMessageDatabaseService - .setMessageDeliveryByMessageId(domMessage.id, - null, - DELIVERY_STATE_ERROR, - RIL.GECKO_SMS_DELIVERY_STATUS_ERROR, - null, - function notifyResult(rv, domMessage) { - // TODO bug 832140 handle !Components.isSuccessCode(rv) - request.notifySendMessageFailed(errorCode); - Services.obs.notifyObservers(domMessage, kSmsFailedObserverTopic, null); - }); - return; - } - - // Keep current SMS message info for sent/delivered notifications - let context = { - request: request, - sms: domMessage, - requestStatusReport: options.requestStatusReport, - silent: silent - }; - - // This is the entry point starting to send SMS. - gRadioInterface.sendWorkerMessage("sendSMS", options, - (function(context, response) { - if (response.errorMsg) { - // Failed to send SMS out. - let error = Ci.nsIMobileMessageCallback.UNKNOWN_ERROR; - switch (response.errorMsg) { - case RIL.ERROR_RADIO_NOT_AVAILABLE: - error = Ci.nsIMobileMessageCallback.NO_SIGNAL_ERROR; - break; - } - - if (context.silent) { - context.request.notifySendMessageFailed(error); - return false; - } - - gMobileMessageDatabaseService - .setMessageDeliveryByMessageId(context.sms.id, - null, - DELIVERY_STATE_ERROR, - RIL.GECKO_SMS_DELIVERY_STATUS_ERROR, - null, - function notifyResult(rv, domMessage) { - // TODO bug 832140 handle !Components.isSuccessCode(rv) - context.request.notifySendMessageFailed(error); - Services.obs.notifyObservers(domMessage, kSmsFailedObserverTopic, null); - }); - return false; - } // End of send failure. - - if (response.deliveryStatus) { - // Message delivery. - gMobileMessageDatabaseService - .setMessageDeliveryByMessageId(context.sms.id, - null, - context.sms.delivery, - response.deliveryStatus, - null, - function notifyResult(rv, domMessage) { - // TODO bug 832140 handle !Components.isSuccessCode(rv) - let topic = (response.deliveryStatus == RIL.GECKO_SMS_DELIVERY_STATUS_SUCCESS) - ? kSmsDeliverySuccessObserverTopic - : kSmsDeliveryErrorObserverTopic; - Services.obs.notifyObservers(domMessage, topic, null); - }); - - // Send transaction has ended completely. - return false; - } // End of message delivery. - - // Message sent. - if (context.silent) { - // There is no way to modify nsIDOMMozSmsMessage attributes as they are - // read only so we just create a new sms instance to send along with - // the notification. - let sms = context.sms; - context.request.notifyMessageSent( - gMobileMessageService.createSmsMessage(sms.id, - sms.threadId, - DELIVERY_STATE_SENT, - sms.deliveryStatus, - sms.sender, - sms.receiver, - sms.body, - sms.messageClass, - sms.timestamp, - sms.read)); - // We don't wait for SMS-DELIVER-REPORT for silent one. - return false; - } - - gMobileMessageDatabaseService - .setMessageDeliveryByMessageId(context.sms.id, - null, - DELIVERY_STATE_SENT, - context.sms.deliveryStatus, - null, - (function notifyResult(rv, domMessage) { - // TODO bug 832140 handle !Components.isSuccessCode(rv) - this._broadcastSystemMessage("sms-sent", domMessage); - - if (context.requestStatusReport) { - context.sms = domMessage; - } - - context.request.notifyMessageSent(domMessage); - Services.obs.notifyObservers(domMessage, kSmsSentObserverTopic, null); - }).bind(this)); - - // Only keep current context if we have requested for delivery report. - return context.requestStatusReport; - }).bind(this, context)); // End of |sendWorkerMessage| callback. - }).bind(this); // End of DB saveSendingMessage callback. - - let sendingMessage = { - type: "sms", - sender: this._getMsisdn(), - receiver: number, - body: text, - deliveryStatusRequested: options.requestStatusReport, - timestamp: Date.now() - }; - - if (silent) { - let deliveryStatus = RIL.GECKO_SMS_DELIVERY_STATUS_PENDING; - let delivery = DELIVERY_STATE_SENDING; - let domMessage = - gMobileMessageService.createSmsMessage(-1, // id - 0, // threadId - delivery, - deliveryStatus, - sendingMessage.sender, - sendingMessage.receiver, - sendingMessage.body, - "normal", // message class - sendingMessage.timestamp, - false); - notifyResult(Cr.NS_OK, domMessage); - return; - } - - let id = gMobileMessageDatabaseService.saveSendingMessage( - sendingMessage, notifyResult); - }, - - addSilentNumber: function addSilentNumber(number) { - if (this._isSilentNumber(number)) { - throw Cr.NS_ERROR_UNEXPECTED; - } - - this.silentNumbers.push(number); - }, - - removeSilentNumber: function removeSilentNumber(number) { - let index = this.silentNumbers.indexOf(number); - if (index < 0) { - throw Cr.NS_ERROR_INVALID_ARG; - } - - this.silentNumbers.splice(index, 1); - }, - - /** - * nsIRilSmsService methods. - */ - - notifyMessageReceived: function notifyMessageReceived(message) { - if (DEBUG) debug("notifyMessageReceived: " + JSON.stringify(message)); - - // FIXME: Bug 737202 - Typed arrays become normal arrays when sent to/from workers - if (message.encoding == RIL.PDU_DCS_MSG_CODING_8BITS_ALPHABET) { - message.fullData = new Uint8Array(message.fullData); - } - - // Dispatch to registered handler if application port addressing is - // available. Note that the destination port can possibly be zero when - // representing a UDP/TCP port. - if (message.header && message.header.destinationPort != null) { - let handler = this.portAddressedSmsApps[message.header.destinationPort]; - if (handler) { - handler(message); - } - gRadioInterface.sendWorkerMessage("ackSMS", { result: RIL.PDU_FCS_OK }); - return; - } - - if (message.encoding == RIL.PDU_DCS_MSG_CODING_8BITS_ALPHABET) { - // Don't know how to handle binary data yet. - gRadioInterface.sendWorkerMessage("ackSMS", { result: RIL.PDU_FCS_OK }); - return; - } - - message.type = "sms"; - message.sender = message.sender || null; - message.receiver = this._getMsisdn(); - message.body = message.fullBody = message.fullBody || null; - message.timestamp = Date.now(); - - if (this._isSilentNumber(message.sender)) { - message.id = -1; - message.threadId = 0; - message.delivery = DELIVERY_STATE_RECEIVED; - message.deliveryStatus = RIL.GECKO_SMS_DELIVERY_STATUS_SUCCESS; - message.read = false; - - let domMessage = - gMobileMessageService.createSmsMessage(message.id, - message.threadId, - message.delivery, - message.deliveryStatus, - message.sender, - message.receiver, - message.body, - message.messageClass, - message.timestamp, - message.read); - - Services.obs.notifyObservers(domMessage, - kSilentSmsReceivedObserverTopic, - null); - gRadioInterface.sendWorkerMessage("ackSMS", { result: RIL.PDU_FCS_OK }); - return; - } - - // TODO: Bug #768441 - // For now we don't store indicators persistently. When the mwi.discard - // flag is false, we'll need to persist the indicator to EFmwis. - // See TS 23.040 9.2.3.24.2 - - let mwi = message.mwi; - if (mwi) { - mwi.returnNumber = message.sender; - mwi.returnMessage = message.fullBody; - // TODO: Bug 833229 - B2G RIL: use ipdl as IPC in MozVoicemail - gMessageManager.sendVoicemailMessage("RIL:VoicemailNotification", - this.clientId, mwi); - gRadioInterface.sendWorkerMessage("ackSMS", { result: RIL.PDU_FCS_OK }); - return; - } - - let notifyReceived = function notifyReceived(rv, domMessage) { - let success = Components.isSuccessCode(rv); - - // Acknowledge the reception of the SMS. - gRadioInterface.sendWorkerMessage("ackSMS", { - 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) { - debug("Could not store SMS " + message.id + ", error code " + rv); - } - return; - } - - this._broadcastSystemMessage("sms-received", domMessage); - Services.obs.notifyObservers(domMessage, kSmsReceivedObserverTopic, null); - }.bind(this); - - if (message.messageClass == RIL.GECKO_SMS_MESSAGE_CLASSES[RIL.PDU_DCS_MSG_CLASS_0]) { - message.id = -1; - message.threadId = 0; - message.delivery = DELIVERY_STATE_RECEIVED; - message.deliveryStatus = RIL.GECKO_SMS_DELIVERY_STATUS_SUCCESS; - message.read = false; - - let domMessage = - gMobileMessageService.createSmsMessage(message.id, - message.threadId, - message.delivery, - message.deliveryStatus, - message.sender, - message.receiver, - message.body, - message.messageClass, - message.timestamp, - message.read); - - notifyReceived(Cr.NS_OK, domMessage); - return; - } - - message.id = - gMobileMessageDatabaseService.saveReceivedMessage(message, - notifyReceived); - }, - - /** - * nsIObserver methods. - */ - - observe: function observe(subject, topic, data) { - switch (topic) { - case kPrefenceChangedObserverTopic: - if (data === "ril.debugging.enabled") { - this._updateDebugFlag(); - } - break; - - case kXpcomShutdownObserverTopic: - Services.obs.removeObserver(this, kPrefenceChangedObserverTopic); - Services.obs.removeObserver(this, kXpcomShutdownObserverTopic); - break; - } - } -}; - -this.NSGetFactory = XPCOMUtils.generateNSGetFactory([SmsService]); diff --git a/dom/mobilemessage/src/gonk/SmsService.manifest b/dom/mobilemessage/src/gonk/SmsService.manifest deleted file mode 100644 index 0275b5a2be38..000000000000 --- a/dom/mobilemessage/src/gonk/SmsService.manifest +++ /dev/null @@ -1,3 +0,0 @@ -# SmsService.js -component {46a9ed78-3574-40a1-9f12-ea179942d67f} SmsService.js -contract @mozilla.org/sms/rilsmsservice;1 {46a9ed78-3574-40a1-9f12-ea179942d67f} diff --git a/dom/mobilemessage/src/ipc/SmsIPCService.cpp b/dom/mobilemessage/src/ipc/SmsIPCService.cpp index 8b423c50e7f4..876bb2a2f4fc 100644 --- a/dom/mobilemessage/src/ipc/SmsIPCService.cpp +++ b/dom/mobilemessage/src/ipc/SmsIPCService.cpp @@ -115,6 +115,14 @@ SmsIPCService::Send(const nsAString& aNumber, aRequest); } +NS_IMETHODIMP +SmsIPCService::IsSilentNumber(const nsAString& aNumber, + bool* aIsSilent) +{ + NS_ERROR("We should not be here!"); + return NS_ERROR_FAILURE; +} + NS_IMETHODIMP SmsIPCService::AddSilentNumber(const nsAString& aNumber) { diff --git a/dom/mobilemessage/src/moz.build b/dom/mobilemessage/src/moz.build index fa72d68ae99d..505fddd8242d 100644 --- a/dom/mobilemessage/src/moz.build +++ b/dom/mobilemessage/src/moz.build @@ -36,7 +36,6 @@ else: CPP_SOURCES += [ 'MobileMessageDatabaseService.cpp', 'MmsService.cpp', - 'SmsService.cpp', ] EXPORTS.mozilla.dom += [ @@ -61,6 +60,7 @@ CPP_SOURCES += [ 'SmsMessage.cpp', 'SmsParent.cpp', 'SmsSegmentInfo.cpp', + 'SmsService.cpp', 'SmsServicesFactory.cpp', ] @@ -70,8 +70,6 @@ if CONFIG['MOZ_B2G_RIL']: 'gonk/MmsService.manifest', 'gonk/MobileMessageDatabaseService.js', 'gonk/MobileMessageDatabaseService.manifest', - 'gonk/SmsService.js', - 'gonk/SmsService.manifest', ] IPDL_SOURCES += [ diff --git a/dom/moz.build b/dom/moz.build index 4c87f43ef34c..5aeb1ba6a7a5 100644 --- a/dom/moz.build +++ b/dom/moz.build @@ -72,8 +72,7 @@ PARALLEL_DIRS += [ 'camera', 'audiochannel', 'promise', - 'wappush', - 'telephony', + 'wappush' ] if CONFIG['OS_ARCH'] == 'WINNT': @@ -81,6 +80,7 @@ if CONFIG['OS_ARCH'] == 'WINNT': if CONFIG['MOZ_B2G_RIL']: PARALLEL_DIRS += [ + 'telephony', 'wifi', 'icc', 'cellbroadcast', diff --git a/dom/system/gonk/Makefile.in b/dom/system/gonk/Makefile.in index 01378e2f9357..56710dccf091 100644 --- a/dom/system/gonk/Makefile.in +++ b/dom/system/gonk/Makefile.in @@ -17,6 +17,7 @@ include $(topsrcdir)/dom/dom-config.mk LOCAL_INCLUDES = \ -I$(topsrcdir)/dom/base \ -I$(topsrcdir)/dom/src/geolocation \ + -I$(topsrcdir)/dom/telephony \ -I$(topsrcdir)/dom/wifi \ -I$(topsrcdir)/dom/bluetooth \ -I$(topsrcdir)/content/events/src \ diff --git a/dom/system/gonk/RILContentHelper.js b/dom/system/gonk/RILContentHelper.js index 8ae83ecd4033..b7bc9ce4aa9c 100644 --- a/dom/system/gonk/RILContentHelper.js +++ b/dom/system/gonk/RILContentHelper.js @@ -71,13 +71,17 @@ const RIL_IPC_MSG_NAMES = [ "RIL:IccInfoChanged", "RIL:VoiceInfoChanged", "RIL:DataInfoChanged", + "RIL:EnumerateCalls", "RIL:GetAvailableNetworks", "RIL:NetworkSelectionModeChanged", "RIL:SelectNetwork", "RIL:SelectNetworkAuto", + "RIL:CallStateChanged", "RIL:EmergencyCbModeChanged", "RIL:VoicemailNotification", "RIL:VoicemailInfoChanged", + "RIL:CallError", + "RIL:SuppSvcNotification", "RIL:CardLockResult", "RIL:CardLockRetryCount", "RIL:USSDReceived", @@ -104,9 +108,11 @@ const RIL_IPC_MSG_NAMES = [ "RIL:UpdateIccContact", "RIL:SetRoamingPreference", "RIL:GetRoamingPreference", + "RIL:CdmaCallWaiting", "RIL:ExitEmergencyCbMode", "RIL:SetVoicePrivacyMode", "RIL:GetVoicePrivacyMode", + "RIL:ConferenceCallStateChanged", "RIL:OtaStatusChanged" ]; @@ -114,6 +120,10 @@ XPCOMUtils.defineLazyServiceGetter(this, "cpmm", "@mozilla.org/childprocessmessagemanager;1", "nsISyncMessageSender"); +XPCOMUtils.defineLazyServiceGetter(this, "gUUIDGenerator", + "@mozilla.org/uuid-generator;1", + "nsIUUIDGenerator"); + function MobileIccCardLockResult(options) { this.lockType = options.lockType; this.enabled = options.enabled; @@ -439,6 +449,7 @@ RILContentHelper.prototype = { QueryInterface: XPCOMUtils.generateQI([Ci.nsIMobileConnectionProvider, Ci.nsICellBroadcastProvider, Ci.nsIVoicemailProvider, + Ci.nsITelephonyProvider, Ci.nsIIccProvider, Ci.nsIObserver, Ci.nsISupportsWeakReference]), @@ -448,6 +459,7 @@ RILContentHelper.prototype = { interfaces: [Ci.nsIMobileConnectionProvider, Ci.nsICellBroadcastProvider, Ci.nsIVoicemailProvider, + Ci.nsITelephonyProvider, Ci.nsIIccProvider]}), // An utility function to copy objects. @@ -1269,9 +1281,11 @@ RILContentHelper.prototype = { }, _mobileConnectionListeners: null, + _telephonyListeners: null, _cellBroadcastListeners: null, _voicemailListeners: null, _iccListeners: null, + _enumerateTelephonyCallbacks: null, voicemailStatus: null, @@ -1333,6 +1347,24 @@ RILContentHelper.prototype = { this.unregisterListener("_mobileConnectionListeners", listener); }, + registerTelephonyMsg: function registerTelephonyMsg(listener) { + debug("Registering for telephony-related messages"); + this.registerListener("_telephonyListeners", listener); + cpmm.sendAsyncMessage("RIL:RegisterTelephonyMsg"); + }, + + unregisterTelephonyMsg: function unregisteTelephonyMsg(listener) { + this.unregisterListener("_telephonyListeners", listener); + + // We also need to make sure the listener is removed from + // _enumerateTelephonyCallbacks. + let index = this._enumerateTelephonyCallbacks.indexOf(listener); + if (index != -1) { + this._enumerateTelephonyCallbacks.splice(index, 1); + if (DEBUG) debug("Unregistered enumerateTelephony callback: " + listener); + } + }, + registerVoicemailMsg: function registerVoicemailMsg(listener) { debug("Registering for voicemail-related messages"); this.registerListener("_voicemailListeners", listener); @@ -1363,6 +1395,135 @@ RILContentHelper.prototype = { this.unregisterListener("_iccListeners", listener); }, + enumerateCalls: function enumerateCalls(callback) { + debug("Requesting enumeration of calls for callback: " + callback); + // We need 'requestId' to meet the 'RILContentHelper <--> RadioInterfaceLayer' + // protocol. + let requestId = this._getRandomId(); + cpmm.sendAsyncMessage("RIL:EnumerateCalls", { + clientId: 0, + data: { + requestId: requestId + } + }); + if (!this._enumerateTelephonyCallbacks) { + this._enumerateTelephonyCallbacks = []; + } + this._enumerateTelephonyCallbacks.push(callback); + }, + + startTone: function startTone(dtmfChar) { + debug("Sending Tone for " + dtmfChar); + cpmm.sendAsyncMessage("RIL:StartTone", { + clientId: 0, + data: dtmfChar + }); + }, + + stopTone: function stopTone() { + debug("Stopping Tone"); + cpmm.sendAsyncMessage("RIL:StopTone", {clientId: 0}); + }, + + dial: function dial(number) { + debug("Dialing " + number); + cpmm.sendAsyncMessage("RIL:Dial", { + clientId: 0, + data: number + }); + }, + + dialEmergency: function dialEmergency(number) { + debug("Dialing emergency " + number); + cpmm.sendAsyncMessage("RIL:DialEmergency", { + clientId: 0, + data: number + }); + }, + + hangUp: function hangUp(callIndex) { + debug("Hanging up call no. " + callIndex); + cpmm.sendAsyncMessage("RIL:HangUp", { + clientId: 0, + data: callIndex + }); + }, + + answerCall: function answerCall(callIndex) { + cpmm.sendAsyncMessage("RIL:AnswerCall", { + clientId: 0, + data: callIndex + }); + }, + + rejectCall: function rejectCall(callIndex) { + cpmm.sendAsyncMessage("RIL:RejectCall", { + clientId: 0, + data: callIndex + }); + }, + + holdCall: function holdCall(callIndex) { + cpmm.sendAsyncMessage("RIL:HoldCall", { + clientId: 0, + data: callIndex + }); + }, + + resumeCall: function resumeCall(callIndex) { + cpmm.sendAsyncMessage("RIL:ResumeCall", { + clientId: 0, + data: callIndex + }); + }, + + conferenceCall: function conferenceCall() { + cpmm.sendAsyncMessage("RIL:ConferenceCall", { + clientId: 0 + }); + }, + + separateCall: function separateCall(callIndex) { + cpmm.sendAsyncMessage("RIL:SeparateCall", { + clientId: 0, + data: callIndex + }); + }, + + holdConference: function holdConference() { + cpmm.sendAsyncMessage("RIL:HoldConference", { + clientId: 0 + }); + }, + + resumeConference: function resumeConference() { + cpmm.sendAsyncMessage("RIL:ResumeConference", { + clientId: 0 + }); + }, + + get microphoneMuted() { + return cpmm.sendSyncMessage("RIL:GetMicrophoneMuted", {clientId: 0})[0]; + }, + + set microphoneMuted(value) { + cpmm.sendAsyncMessage("RIL:SetMicrophoneMuted", { + clientId: 0, + data: value + }); + }, + + get speakerEnabled() { + return cpmm.sendSyncMessage("RIL:GetSpeakerEnabled", {clientId: 0})[0]; + }, + + set speakerEnabled(value) { + cpmm.sendAsyncMessage("RIL:SetSpeakerEnabled", { + clientId: 0, + data: value + }); + }, + // nsIObserver observe: function observe(subject, topic, data) { @@ -1459,6 +1620,9 @@ RILContentHelper.prototype = { "notifyOtaStatusChanged", [msg.json.data]); break; + case "RIL:EnumerateCalls": + this.handleEnumerateCalls(msg.json.calls); + break; case "RIL:GetAvailableNetworks": this.handleGetAvailableNetworks(msg.json); break; @@ -1473,6 +1637,35 @@ RILContentHelper.prototype = { this.handleSelectNetwork(msg.json, RIL.GECKO_NETWORK_SELECTION_AUTOMATIC); break; + case "RIL:CallStateChanged": { + let data = msg.json.data; + this._deliverEvent("_telephonyListeners", + "callStateChanged", + [data.callIndex, data.state, + data.number, data.isActive, + data.isOutgoing, data.isEmergency, + data.isConference]); + break; + } + case "RIL:ConferenceCallStateChanged": { + let data = msg.json.data; + this._deliverEvent("_telephonyListeners", + "conferenceCallStateChanged", + [data]); + break; + } + case "RIL:CallError": { + let data = msg.json.data; + this._deliverEvent("_telephonyListeners", + "notifyError", + [data.callIndex, data.errorMsg]); + break; + } + case "RIL:SuppSvcNotification": + this._deliverEvent("_telephonyListeners", + "supplementaryServiceNotification", + [msg.json.callIndex, msg.json.notification]); + break; case "RIL:VoicemailNotification": this.handleVoicemailNotification(msg.json.data); break; @@ -1593,6 +1786,11 @@ RILContentHelper.prototype = { this.handleSimpleRequest(msg.json.requestId, msg.json.errorMsg, msg.json.mode); break; + case "RIL:CdmaCallWaiting": + this._deliverEvent("_telephonyListeners", + "notifyCdmaCallWaiting", + [msg.json.data]); + break; case "RIL:ExitEmergencyCbMode": this.handleExitEmergencyCbMode(msg.json); break; @@ -1612,6 +1810,35 @@ RILContentHelper.prototype = { } }, + handleEnumerateCalls: function handleEnumerateCalls(calls) { + debug("handleEnumerateCalls: " + JSON.stringify(calls)); + let callback = this._enumerateTelephonyCallbacks.shift(); + if (!calls.length) { + callback.enumerateCallStateComplete(); + return; + } + + for (let i in calls) { + let call = calls[i]; + let keepGoing; + try { + keepGoing = + callback.enumerateCallState(call.callIndex, call.state, call.number, + call.isActive, call.isOutgoing, + call.isEmergency, call.isConference); + } catch (e) { + debug("callback handler for 'enumerateCallState' threw an " + + " exception: " + e); + keepGoing = true; + } + if (!keepGoing) { + break; + } + } + + callback.enumerateCallStateComplete(); + }, + handleSimpleRequest: function handleSimpleRequest(requestId, errorMsg, result) { if (errorMsg) { this.fireRequestError(requestId, errorMsg); @@ -1824,6 +2051,10 @@ RILContentHelper.prototype = { } }, + _getRandomId: function _getRandomId() { + return gUUIDGenerator.generateUUID().toString(); + }, + _deliverEvent: function _deliverEvent(listenerType, name, args) { let thisListeners = this[listenerType]; if (!thisListeners) { diff --git a/dom/system/gonk/RadioInterfaceLayer.js b/dom/system/gonk/RadioInterfaceLayer.js index 3af1c0747558..f1d57725fee6 100644 --- a/dom/system/gonk/RadioInterfaceLayer.js +++ b/dom/system/gonk/RadioInterfaceLayer.js @@ -46,7 +46,17 @@ const RADIOINTERFACE_CID = const RILNETWORKINTERFACE_CID = Components.ID("{3bdd52a9-3965-4130-b569-0ac5afed045e}"); +const nsIAudioManager = Ci.nsIAudioManager; +const nsITelephonyProvider = Ci.nsITelephonyProvider; + const kNetworkInterfaceStateChangedTopic = "network-interface-state-changed"; +const kSmsReceivedObserverTopic = "sms-received"; +const kSilentSmsReceivedObserverTopic = "silent-sms-received"; +const kSmsSendingObserverTopic = "sms-sending"; +const kSmsSentObserverTopic = "sms-sent"; +const kSmsFailedObserverTopic = "sms-failed"; +const kSmsDeliverySuccessObserverTopic = "sms-delivery-success"; +const kSmsDeliveryErrorObserverTopic = "sms-delivery-error"; const kMozSettingsChangedObserverTopic = "mozsettings-changed"; const kSysMsgListenerReadyObserverTopic = "system-message-listener-ready"; const kSysClockChangeObserverTopic = "system-clock-change"; @@ -58,7 +68,35 @@ const kCellBroadcastDisabled = "ril.cellbroadcast.disabled"; const kPrefenceChangedObserverTopic = "nsPref:changed"; const kClirModePreference = "ril.clirMode"; -const RADIO_POWER_OFF_TIMEOUT = 30000; +const DOM_MOBILE_MESSAGE_DELIVERY_RECEIVED = "received"; +const DOM_MOBILE_MESSAGE_DELIVERY_SENDING = "sending"; +const DOM_MOBILE_MESSAGE_DELIVERY_SENT = "sent"; +const DOM_MOBILE_MESSAGE_DELIVERY_ERROR = "error"; + +const CALL_WAKELOCK_TIMEOUT = 5000; +const RADIO_POWER_OFF_TIMEOUT = 30000; + +const RIL_IPC_TELEPHONY_MSG_NAMES = [ + "RIL:EnumerateCalls", + "RIL:GetMicrophoneMuted", + "RIL:SetMicrophoneMuted", + "RIL:GetSpeakerEnabled", + "RIL:SetSpeakerEnabled", + "RIL:StartTone", + "RIL:StopTone", + "RIL:Dial", + "RIL:DialEmergency", + "RIL:HangUp", + "RIL:AnswerCall", + "RIL:RejectCall", + "RIL:HoldCall", + "RIL:ResumeCall", + "RIL:RegisterTelephonyMsg", + "RIL:ConferenceCall", + "RIL:SeparateCall", + "RIL:HoldConference", + "RIL:ResumeConference" +]; const RIL_IPC_MOBILECONNECTION_MSG_NAMES = [ "RIL:GetRilContext", @@ -67,6 +105,7 @@ const RIL_IPC_MOBILECONNECTION_MSG_NAMES = [ "RIL:SelectNetworkAuto", "RIL:SendMMI", "RIL:CancelMMI", + "RIL:RegisterMobileConnectionMsg", "RIL:SetCallForwardingOption", "RIL:GetCallForwardingOption", "RIL:SetCallBarringOption", @@ -96,16 +135,38 @@ const RIL_IPC_ICCMANAGER_MSG_NAMES = [ "RIL:IccExchangeAPDU", "RIL:IccCloseChannel", "RIL:ReadIccContacts", - "RIL:UpdateIccContact" + "RIL:UpdateIccContact", + "RIL:RegisterIccMsg" ]; const RIL_IPC_VOICEMAIL_MSG_NAMES = [ + "RIL:RegisterVoicemailMsg", "RIL:GetVoicemailInfo" ]; +const RIL_IPC_CELLBROADCAST_MSG_NAMES = [ + "RIL:RegisterCellBroadcastMsg" +]; + +XPCOMUtils.defineLazyServiceGetter(this, "gPowerManagerService", + "@mozilla.org/power/powermanagerservice;1", + "nsIPowerManagerService"); + +XPCOMUtils.defineLazyServiceGetter(this, "gMobileMessageService", + "@mozilla.org/mobilemessage/mobilemessageservice;1", + "nsIMobileMessageService"); + XPCOMUtils.defineLazyServiceGetter(this, "gSmsService", - "@mozilla.org/sms/rilsmsservice;1", - "nsIRilSmsService"); + "@mozilla.org/sms/smsservice;1", + "nsISmsService"); + +XPCOMUtils.defineLazyServiceGetter(this, "gMobileMessageDatabaseService", + "@mozilla.org/mobilemessage/rilmobilemessagedatabaseservice;1", + "nsIRilMobileMessageDatabaseService"); + +XPCOMUtils.defineLazyServiceGetter(this, "ppmm", + "@mozilla.org/parentprocessmessagemanager;1", + "nsIMessageBroadcaster"); XPCOMUtils.defineLazyServiceGetter(this, "gSettingsService", "@mozilla.org/settingsService;1", @@ -127,27 +188,376 @@ XPCOMUtils.defineLazyServiceGetter(this, "gSystemWorkerManager", "@mozilla.org/telephony/system-worker-manager;1", "nsISystemWorkerManager"); -XPCOMUtils.defineLazyServiceGetter(this, "gTelephonyProvider", - "@mozilla.org/telephony/telephonyprovider;1", - "nsIGonkTelephonyProvider"); +XPCOMUtils.defineLazyGetter(this, "WAP", function () { + let wap = {}; + Cu.import("resource://gre/modules/WapPushManager.js", wap); + return wap; +}); + +XPCOMUtils.defineLazyGetter(this, "PhoneNumberUtils", function () { + let ns = {}; + Cu.import("resource://gre/modules/PhoneNumberUtils.jsm", ns); + return ns.PhoneNumberUtils; +}); + +function convertRILCallState(state) { + switch (state) { + case RIL.CALL_STATE_ACTIVE: + return nsITelephonyProvider.CALL_STATE_CONNECTED; + case RIL.CALL_STATE_HOLDING: + return nsITelephonyProvider.CALL_STATE_HELD; + case RIL.CALL_STATE_DIALING: + return nsITelephonyProvider.CALL_STATE_DIALING; + case RIL.CALL_STATE_ALERTING: + return nsITelephonyProvider.CALL_STATE_ALERTING; + case RIL.CALL_STATE_INCOMING: + case RIL.CALL_STATE_WAITING: + return nsITelephonyProvider.CALL_STATE_INCOMING; + default: + throw new Error("Unknown rilCallState: " + state); + } +} + +function convertRILSuppSvcNotification(notification) { + switch (notification) { + case RIL.GECKO_SUPP_SVC_NOTIFICATION_REMOTE_HELD: + return nsITelephonyProvider.NOTIFICATION_REMOTE_HELD; + case RIL.GECKO_SUPP_SVC_NOTIFICATION_REMOTE_RESUMED: + return nsITelephonyProvider.NOTIFICATION_REMOTE_RESUMED; + default: + throw new Error("Unknown rilSuppSvcNotification: " + notification); + } +} + +/** + * Fake nsIAudioManager implementation so that we can run the telephony + * code in a non-Gonk build. + */ +let FakeAudioManager = { + microphoneMuted: false, + masterVolume: 1.0, + masterMuted: false, + phoneState: nsIAudioManager.PHONE_STATE_CURRENT, + _forceForUse: {}, + setForceForUse: function setForceForUse(usage, force) { + this._forceForUse[usage] = force; + }, + getForceForUse: function setForceForUse(usage) { + return this._forceForUse[usage] || nsIAudioManager.FORCE_NONE; + } +}; + +XPCOMUtils.defineLazyGetter(this, "gAudioManager", function getAudioManager() { + try { + return Cc["@mozilla.org/telephony/audiomanager;1"] + .getService(nsIAudioManager); + } catch (ex) { + //TODO on the phone this should not fall back as silently. + if (DEBUG) debug("Using fake audio manager."); + return FakeAudioManager; + } +}); XPCOMUtils.defineLazyGetter(this, "gMessageManager", function () { - let ns = {}; - Cu.import("resource://gre/modules/RilMessageManager.jsm", ns); - return ns.RilMessageManager; + return { + QueryInterface: XPCOMUtils.generateQI([Ci.nsIMessageListener, + Ci.nsIObserver]), + + ril: null, + + // Manage message targets in terms of topic. Only the authorized and + // registered contents can receive related messages. + targetsByTopic: {}, + topics: [], + + targetMessageQueue: [], + ready: false, + + init: function init(ril) { + this.ril = ril; + + Services.obs.addObserver(this, "xpcom-shutdown", false); + Services.obs.addObserver(this, kSysMsgListenerReadyObserverTopic, false); + this._registerMessageListeners(); + }, + + _shutdown: function _shutdown() { + this.ril = null; + + Services.obs.removeObserver(this, "xpcom-shutdown"); + this._unregisterMessageListeners(); + }, + + _registerMessageListeners: function _registerMessageListeners() { + ppmm.addMessageListener("child-process-shutdown", this); + for (let msgname of RIL_IPC_TELEPHONY_MSG_NAMES) { + ppmm.addMessageListener(msgname, this); + } + for (let msgname of RIL_IPC_MOBILECONNECTION_MSG_NAMES) { + ppmm.addMessageListener(msgname, this); + } + for (let msgName of RIL_IPC_ICCMANAGER_MSG_NAMES) { + ppmm.addMessageListener(msgName, this); + } + for (let msgname of RIL_IPC_VOICEMAIL_MSG_NAMES) { + ppmm.addMessageListener(msgname, this); + } + for (let msgname of RIL_IPC_CELLBROADCAST_MSG_NAMES) { + ppmm.addMessageListener(msgname, this); + } + }, + + _unregisterMessageListeners: function _unregisterMessageListeners() { + ppmm.removeMessageListener("child-process-shutdown", this); + for (let msgname of RIL_IPC_TELEPHONY_MSG_NAMES) { + ppmm.removeMessageListener(msgname, this); + } + for (let msgname of RIL_IPC_MOBILECONNECTION_MSG_NAMES) { + ppmm.removeMessageListener(msgname, this); + } + for (let msgName of RIL_IPC_ICCMANAGER_MSG_NAMES) { + ppmm.removeMessageListener(msgName, this); + } + for (let msgname of RIL_IPC_VOICEMAIL_MSG_NAMES) { + ppmm.removeMessageListener(msgname, this); + } + for (let msgname of RIL_IPC_CELLBROADCAST_MSG_NAMES) { + ppmm.removeMessageListener(msgname, this); + } + ppmm = null; + }, + + _registerMessageTarget: function _registerMessageTarget(topic, target) { + let targets = this.targetsByTopic[topic]; + if (!targets) { + targets = this.targetsByTopic[topic] = []; + let list = this.topics; + if (list.indexOf(topic) == -1) { + list.push(topic); + } + } + + if (targets.indexOf(target) != -1) { + if (DEBUG) debug("Already registered this target!"); + return; + } + + targets.push(target); + if (DEBUG) debug("Registered " + topic + " target: " + target); + }, + + _unregisterMessageTarget: function _unregisterMessageTarget(topic, target) { + if (topic == null) { + // Unregister the target for every topic when no topic is specified. + for (let type of this.topics) { + this._unregisterMessageTarget(type, target); + } + return; + } + + // Unregister the target for a specified topic. + let targets = this.targetsByTopic[topic]; + if (!targets) { + return; + } + + let index = targets.indexOf(target); + if (index != -1) { + targets.splice(index, 1); + if (DEBUG) debug("Unregistered " + topic + " target: " + target); + } + }, + + _enqueueTargetMessage: function _enqueueTargetMessage(topic, message, options) { + let msg = { topic : topic, + message : message, + options : options }; + // Remove previous queued message of same message type, only one message + // per message type is allowed in queue. + let messageQueue = this.targetMessageQueue; + for(let i = 0; i < messageQueue.length; i++) { + if (messageQueue[i].message === message) { + messageQueue.splice(i, 1); + break; + } + } + + messageQueue.push(msg); + }, + + _sendTargetMessage: function _sendTargetMessage(topic, message, options) { + if (!this.ready) { + this._enqueueTargetMessage(topic, message, options); + return; + } + + let targets = this.targetsByTopic[topic]; + if (!targets) { + return; + } + + for (let target of targets) { + target.sendAsyncMessage(message, options); + } + }, + + _resendQueuedTargetMessage: function _resendQueuedTargetMessage() { + this.ready = true; + + // Here uses this._sendTargetMessage() to resend message, which will + // enqueue message if listener is not ready. + // So only resend after listener is ready, or it will cause infinate loop and + // hang the system. + + // Dequeue and resend messages. + for each (let msg in this.targetMessageQueue) { + this._sendTargetMessage(msg.topic, msg.message, msg.options); + } + this.targetMessageQueue = null; + }, + + /** + * nsIMessageListener interface methods. + */ + + receiveMessage: function receiveMessage(msg) { + if (DEBUG) debug("Received '" + msg.name + "' message from content process"); + if (msg.name == "child-process-shutdown") { + // By the time we receive child-process-shutdown, the child process has + // already forgotten its permissions so we need to unregister the target + // for every permission. + this._unregisterMessageTarget(null, msg.target); + return; + } + + if (RIL_IPC_TELEPHONY_MSG_NAMES.indexOf(msg.name) != -1) { + if (!msg.target.assertPermission("telephony")) { + if (DEBUG) { + debug("Telephony message " + msg.name + + " from a content process with no 'telephony' privileges."); + } + return null; + } + } else if (RIL_IPC_MOBILECONNECTION_MSG_NAMES.indexOf(msg.name) != -1) { + if (!msg.target.assertPermission("mobileconnection")) { + if (DEBUG) { + debug("MobileConnection message " + msg.name + + " from a content process with no 'mobileconnection' privileges."); + } + return null; + } + } else if (RIL_IPC_ICCMANAGER_MSG_NAMES.indexOf(msg.name) != -1) { + if (!msg.target.assertPermission("mobileconnection")) { + if (DEBUG) { + debug("IccManager message " + msg.name + + " from a content process with no 'mobileconnection' privileges."); + } + return null; + } + } else if (RIL_IPC_VOICEMAIL_MSG_NAMES.indexOf(msg.name) != -1) { + if (!msg.target.assertPermission("voicemail")) { + if (DEBUG) { + debug("Voicemail message " + msg.name + + " from a content process with no 'voicemail' privileges."); + } + return null; + } + } else if (RIL_IPC_CELLBROADCAST_MSG_NAMES.indexOf(msg.name) != -1) { + if (!msg.target.assertPermission("cellbroadcast")) { + if (DEBUG) { + debug("Cell Broadcast message " + msg.name + + " from a content process with no 'cellbroadcast' privileges."); + } + return null; + } + } else { + if (DEBUG) debug("Ignoring unknown message type: " + msg.name); + return null; + } + + switch (msg.name) { + case "RIL:RegisterTelephonyMsg": + this._registerMessageTarget("telephony", msg.target); + return; + case "RIL:RegisterMobileConnectionMsg": + this._registerMessageTarget("mobileconnection", msg.target); + return; + case "RIL:RegisterIccMsg": + this._registerMessageTarget("icc", msg.target); + return; + case "RIL:RegisterVoicemailMsg": + this._registerMessageTarget("voicemail", msg.target); + return; + case "RIL:RegisterCellBroadcastMsg": + this._registerMessageTarget("cellbroadcast", msg.target); + return; + } + + let clientId = msg.json.clientId || 0; + let radioInterface = this.ril.getRadioInterface(clientId); + if (!radioInterface) { + if (DEBUG) debug("No such radio interface: " + clientId); + return null; + } + + return radioInterface.receiveMessage(msg); + }, + + /** + * nsIObserver interface methods. + */ + + observe: function observe(subject, topic, data) { + switch (topic) { + case kSysMsgListenerReadyObserverTopic: + Services.obs.removeObserver(this, kSysMsgListenerReadyObserverTopic); + this._resendQueuedTargetMessage(); + break; + case "xpcom-shutdown": + this._shutdown(); + break; + } + }, + + sendTelephonyMessage: function sendTelephonyMessage(message, clientId, data) { + this._sendTargetMessage("telephony", message, { + clientId: clientId, + data: data + }); + }, + + sendMobileConnectionMessage: function sendMobileConnectionMessage(message, clientId, data) { + this._sendTargetMessage("mobileconnection", message, { + clientId: clientId, + data: data + }); + }, + + sendVoicemailMessage: function sendVoicemailMessage(message, clientId, data) { + this._sendTargetMessage("voicemail", message, { + clientId: clientId, + data: data + }); + }, + + sendCellBroadcastMessage: function sendCellBroadcastMessage(message, clientId, data) { + this._sendTargetMessage("cellbroadcast", message, { + clientId: clientId, + data: data + }); + }, + + sendIccMessage: function sendIccMessage(message, clientId, data) { + this._sendTargetMessage("icc", message, { + clientId: clientId, + data: data + }); + } + }; }); function RadioInterfaceLayer() { - let callback = this._receiveMessage.bind(this); - gMessageManager.registerMessageListeners("icc", - RIL_IPC_ICCMANAGER_MSG_NAMES, - callback); - gMessageManager.registerMessageListeners("mobileconnection", - RIL_IPC_MOBILECONNECTION_MSG_NAMES, - callback); - gMessageManager.registerMessageListeners("voicemail", - RIL_IPC_VOICEMAIL_MSG_NAMES, - callback); + gMessageManager.init(this); let options = { debug: debugPref, @@ -182,17 +592,6 @@ RadioInterfaceLayer.prototype = { QueryInterface: XPCOMUtils.generateQI([Ci.nsIRadioInterfaceLayer, Ci.nsIObserver]), - _receiveMessage: function _receiveMessage(topic, msg) { - let clientId = msg.json.clientId || 0; - let radioInterface = this.getRadioInterface(clientId); - if (!radioInterface) { - if (DEBUG) debug("No such radio interface: " + clientId); - return null; - } - - return radioInterface.receiveMessage(msg); - }, - /** * nsIObserver interface methods. */ @@ -431,6 +830,9 @@ function RadioInterface(options) { Services.obs.addObserver(this, kScreenStateChangedTopic, false); Services.prefs.addObserver(kCellBroadcastDisabled, this, false); + + this.portAddressedSmsApps = {}; + this.portAddressedSmsApps[WAP.WDP_PORT_PUSH] = this.handleSmsWdpPortPush.bind(this); } RadioInterface.prototype = { @@ -492,6 +894,61 @@ RadioInterface.prototype = { case "RIL:GetRilContext": // This message is sync. return this.rilContext; + case "RIL:EnumerateCalls": + this.enumerateCalls(msg.target, msg.json.data); + break; + case "RIL:GetMicrophoneMuted": + // This message is sync. + return this.microphoneMuted; + case "RIL:SetMicrophoneMuted": + this.microphoneMuted = msg.json.data; + break; + case "RIL:GetSpeakerEnabled": + // This message is sync. + return this.speakerEnabled; + case "RIL:SetSpeakerEnabled": + this.speakerEnabled = msg.json.data; + break; + case "RIL:StartTone": + this.workerMessenger.send("startTone", { dtmfChar: msg.json.data }); + break; + case "RIL:StopTone": + this.workerMessenger.send("stopTone"); + break; + case "RIL:Dial": + this.dial(msg.json.data); + break; + case "RIL:DialEmergency": + this.dialEmergency(msg.json.data); + break; + case "RIL:HangUp": + this.workerMessenger.send("hangUp", { callIndex: msg.json.data }); + break; + case "RIL:AnswerCall": + this.workerMessenger.send("answerCall", { callIndex: msg.json.data }); + break; + case "RIL:RejectCall": + this.workerMessenger.send("rejectCall", { callIndex: msg.json.data }); + break; + case "RIL:HoldCall": + this.workerMessenger.send("holdCall", { callIndex: msg.json.data }); + break; + case "RIL:ResumeCall": + this.workerMessenger.send("resumeCall", { callIndex: msg.json.data }); + break; + case "RIL:ConferenceCall": + this.workerMessenger.send("conferenceCall"); + break; + case "RIL:SeparateCall": + this.workerMessenger.send("separateCall", + { callIndex: msg.json.data }); + break; + case "RIL:HoldConference": + this.workerMessenger.send("holdConference"); + break; + case "RIL:ResumeConference": + this.workerMessenger.send("resumeConference"); + break; case "RIL:GetAvailableNetworks": this.workerMessenger.sendWithIPCMessage(msg, "getAvailableNetworks"); break; @@ -601,26 +1058,28 @@ RadioInterface.prototype = { handleUnsolicitedWorkerMessage: function handleUnsolicitedWorkerMessage(message) { switch (message.rilMessageType) { case "callRing": - gTelephonyProvider.notifyCallRing(); + this.handleCallRing(); break; case "callStateChange": - gTelephonyProvider.notifyCallStateChanged(message.call); + // This one will handle its own notifications. + this.handleCallStateChange(message.call); break; case "callDisconnected": - gTelephonyProvider.notifyCallDisconnected(message.call); + // This one will handle its own notifications. + this.handleCallDisconnected(message.call); break; case "conferenceCallStateChanged": - gTelephonyProvider.notifyConferenceCallStateChanged(message.state); + this.handleConferenceCallStateChanged(message.state); break; case "cdmaCallWaiting": - gTelephonyProvider.notifyCdmaCallWaiting(message.number); + gMessageManager.sendTelephonyMessage("RIL:CdmaCallWaiting", + this.clientId, message.number); break; case "callError": - gTelephonyProvider.notifyCallError(message.callIndex, message.errorMsg); + this.handleCallError(message); break; case "suppSvcNotification": - gTelephonyProvider.notifySupplementaryService(message.callIndex, - message.notification); + this.handleSuppSvcNotification(message); break; case "emergencyCbModeChange": this.handleEmergencyCbModeChange(message); @@ -658,8 +1117,11 @@ RadioInterface.prototype = { this.clientId, message); break; case "sms-received": - gSmsService.notifyMessageReceived(message); - break; + let ackOk = this.handleSmsReceived(message); + if (ackOk) { + this.workerMessenger.send("ackSMS", { result: RIL.PDU_FCS_OK }); + } + return; case "cellbroadcast-received": message.timestamp = Date.now(); gMessageManager.sendCellBroadcastMessage("RIL:CellBroadcastReceived", @@ -706,6 +1168,18 @@ RadioInterface.prototype = { } }, + getMsisdn: function getMsisdn() { + let iccInfo = this.rilContext.iccInfo; + let number = iccInfo ? iccInfo.msisdn : null; + + // Workaround an xpconnect issue with undefined string objects. + // See bug 808220 + if (number === undefined || number === "undefined") { + return null; + } + return number; + }, + updateNetworkInfo: function updateNetworkInfo(message) { let voiceMessage = message[RIL.NETWORK_INFO_VOICE_REGISTRATION_STATE]; let dataMessage = message[RIL.NETWORK_INFO_DATA_REGISTRATION_STATE]; @@ -1253,6 +1727,165 @@ RadioInterface.prototype = { this.setupDataCallByType("default"); }, + /** + * Track the active call and update the audio system as its state changes. + */ + _activeCall: null, + updateCallAudioState: function updateCallAudioState(options) { + if (options.conferenceState === nsITelephonyProvider.CALL_STATE_CONNECTED) { + gAudioManager.phoneState = nsIAudioManager.PHONE_STATE_IN_CALL; + if (this.speakerEnabled) { + gAudioManager.setForceForUse(nsIAudioManager.USE_COMMUNICATION, + nsIAudioManager.FORCE_SPEAKER); + } + return; + } + if (options.conferenceState === nsITelephonyProvider.CALL_STATE_UNKNOWN || + options.conferenceState === nsITelephonyProvider.CALL_STATE_HELD) { + if (!this._activeCall) { + gAudioManager.phoneState = nsIAudioManager.PHONE_STATE_NORMAL; + } + return; + } + + if (!options.call) { + return; + } + + if (options.call.isConference) { + if (this._activeCall && this._activeCall.callIndex == options.call.callIndex) { + this._activeCall = null; + } + return; + } + + let call = options.call; + switch (call.state) { + case nsITelephonyProvider.CALL_STATE_DIALING: // Fall through... + case nsITelephonyProvider.CALL_STATE_ALERTING: + case nsITelephonyProvider.CALL_STATE_CONNECTED: + call.isActive = true; + this._activeCall = call; + gAudioManager.phoneState = nsIAudioManager.PHONE_STATE_IN_CALL; + if (this.speakerEnabled) { + gAudioManager.setForceForUse(nsIAudioManager.USE_COMMUNICATION, + nsIAudioManager.FORCE_SPEAKER); + } + if (DEBUG) { + this.debug("Active call, put audio system into PHONE_STATE_IN_CALL: " + + gAudioManager.phoneState); + } + break; + case nsITelephonyProvider.CALL_STATE_INCOMING: + call.isActive = false; + if (!this._activeCall) { + // We can change the phone state into RINGTONE only when there's + // no active call. + gAudioManager.phoneState = nsIAudioManager.PHONE_STATE_RINGTONE; + if (DEBUG) { + this.debug("Incoming call, put audio system into " + + "PHONE_STATE_RINGTONE: " + gAudioManager.phoneState); + } + } + break; + case nsITelephonyProvider.CALL_STATE_HELD: // Fall through... + case nsITelephonyProvider.CALL_STATE_DISCONNECTED: + call.isActive = false; + if (this._activeCall && + this._activeCall.callIndex == call.callIndex) { + // Previously active call is not active now. + this._activeCall = null; + } + + if (!this._activeCall) { + // No active call. Disable the audio. + gAudioManager.phoneState = nsIAudioManager.PHONE_STATE_NORMAL; + if (DEBUG) { + this.debug("No active call, put audio system into " + + "PHONE_STATE_NORMAL: " + gAudioManager.phoneState); + } + } + break; + } + }, + + _callRingWakeLock: null, + _callRingWakeLockTimer: null, + _cancelCallRingWakeLockTimer: function _cancelCallRingWakeLockTimer() { + if (this._callRingWakeLockTimer) { + this._callRingWakeLockTimer.cancel(); + } + if (this._callRingWakeLock) { + this._callRingWakeLock.unlock(); + this._callRingWakeLock = null; + } + }, + + /** + * Handle an incoming call. + * + * Not much is known about this call at this point, but it's enough + * to start bringing up the Phone app already. + */ + handleCallRing: function handleCallRing() { + if (!this._callRingWakeLock) { + this._callRingWakeLock = gPowerManagerService.newWakeLock("cpu"); + } + if (!this._callRingWakeLockTimer) { + this._callRingWakeLockTimer = + Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); + } + this._callRingWakeLockTimer + .initWithCallback(this._cancelCallRingWakeLockTimer.bind(this), + CALL_WAKELOCK_TIMEOUT, Ci.nsITimer.TYPE_ONE_SHOT); + + gSystemMessenger.broadcastMessage("telephony-new-call", {}); + }, + + /** + * Handle call state changes by updating our current state and the audio + * system. + */ + handleCallStateChange: function handleCallStateChange(call) { + if (DEBUG) this.debug("handleCallStateChange: " + JSON.stringify(call)); + call.state = convertRILCallState(call.state); + + if (call.state == nsITelephonyProvider.CALL_STATE_DIALING) { + gSystemMessenger.broadcastMessage("telephony-new-call", {}); + } + this.updateCallAudioState({call: call}); + gMessageManager.sendTelephonyMessage("RIL:CallStateChanged", + this.clientId, call); + }, + + /** + * Handle call disconnects by updating our current state and the audio system. + */ + handleCallDisconnected: function handleCallDisconnected(call) { + if (DEBUG) this.debug("handleCallDisconnected: " + JSON.stringify(call)); + call.state = nsITelephonyProvider.CALL_STATE_DISCONNECTED; + let duration = ("started" in call && typeof call.started == "number") ? + new Date().getTime() - call.started : 0; + let data = { + number: call.number, + duration: duration, + direction: call.isOutgoing ? "outgoing" : "incoming" + }; + gSystemMessenger.broadcastMessage("telephony-call-ended", data); + this.updateCallAudioState({call: call}); + gMessageManager.sendTelephonyMessage("RIL:CallStateChanged", + this.clientId, call); + }, + + handleConferenceCallStateChanged: function handleConferenceCallStateChanged(state) { + debug("handleConferenceCallStateChanged: " + state); + state = state != null ? convertRILCallState(state) : + nsITelephonyProvider.CALL_STATE_UNKNOWN; + this.updateCallAudioState({conferenceState: state}); + gMessageManager.sendTelephonyMessage("RIL:ConferenceCallStateChanged", + this.clientId, state); + }, + /** * Update network selection mode */ @@ -1272,6 +1905,201 @@ RadioInterface.prototype = { this.clientId, message); }, + /** + * Handle call error. + */ + handleCallError: function handleCallError(message) { + gMessageManager.sendTelephonyMessage("RIL:CallError", + this.clientId, message); + }, + + /** + * Handle supplementary service notification. + */ + handleSuppSvcNotification: function handleSuppSvcNotification(message) { + message.notification = convertRILSuppSvcNotification(message.notification); + gMessageManager.sendTelephonyMessage("RIL:SuppSvcNotification", + this.clientId, message); + }, + + /** + * Handle WDP port push PDU. Constructor WDP bearer information and deliver + * to WapPushManager. + * + * @param message + * A SMS message. + */ + handleSmsWdpPortPush: function handleSmsWdpPortPush(message) { + if (message.encoding != RIL.PDU_DCS_MSG_CODING_8BITS_ALPHABET) { + if (DEBUG) { + this.debug("Got port addressed SMS but not encoded in 8-bit alphabet." + + " Drop!"); + } + return; + } + + let options = { + bearer: WAP.WDP_BEARER_GSM_SMS_GSM_MSISDN, + sourceAddress: message.sender, + sourcePort: message.header.originatorPort, + destinationAddress: this.rilContext.iccInfo.msisdn, + destinationPort: message.header.destinationPort, + }; + WAP.WapPushManager.receiveWdpPDU(message.fullData, message.fullData.length, + 0, options); + }, + + /** + * A helper to broadcast the system message to launch registered apps + * like Costcontrol, Notification and Message app... etc. + * + * @param aName + * The system message name. + * @param aDomMessage + * The nsIDOMMozSmsMessage object. + */ + broadcastSmsSystemMessage: function broadcastSmsSystemMessage(aName, aDomMessage) { + if (DEBUG) this.debug("Broadcasting the SMS system message: " + aName); + + // Sadly we cannot directly broadcast the aDomMessage object + // because the system message mechamism will rewrap the object + // based on the content window, which needs to know the properties. + gSystemMessenger.broadcastMessage(aName, { + type: aDomMessage.type, + id: aDomMessage.id, + threadId: aDomMessage.threadId, + delivery: aDomMessage.delivery, + deliveryStatus: aDomMessage.deliveryStatus, + sender: aDomMessage.sender, + receiver: aDomMessage.receiver, + body: aDomMessage.body, + messageClass: aDomMessage.messageClass, + timestamp: aDomMessage.timestamp, + read: aDomMessage.read + }); + }, + + portAddressedSmsApps: null, + handleSmsReceived: function handleSmsReceived(message) { + if (DEBUG) this.debug("handleSmsReceived: " + JSON.stringify(message)); + + // FIXME: Bug 737202 - Typed arrays become normal arrays when sent to/from workers + if (message.encoding == RIL.PDU_DCS_MSG_CODING_8BITS_ALPHABET) { + message.fullData = new Uint8Array(message.fullData); + } + + // Dispatch to registered handler if application port addressing is + // available. Note that the destination port can possibly be zero when + // representing a UDP/TCP port. + if (message.header && message.header.destinationPort != null) { + let handler = this.portAddressedSmsApps[message.header.destinationPort]; + if (handler) { + handler(message); + } + return true; + } + + if (message.encoding == RIL.PDU_DCS_MSG_CODING_8BITS_ALPHABET) { + // Don't know how to handle binary data yet. + return true; + } + + message.type = "sms"; + message.sender = message.sender || null; + message.receiver = this.getMsisdn(); + message.body = message.fullBody = message.fullBody || null; + message.timestamp = Date.now(); + + if (gSmsService.isSilentNumber(message.sender)) { + 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 = + gMobileMessageService.createSmsMessage(message.id, + message.threadId, + message.delivery, + message.deliveryStatus, + message.sender, + message.receiver, + message.body, + message.messageClass, + message.timestamp, + message.read); + + Services.obs.notifyObservers(domMessage, + kSilentSmsReceivedObserverTopic, + null); + return true; + } + + // TODO: Bug #768441 + // For now we don't store indicators persistently. When the mwi.discard + // flag is false, we'll need to persist the indicator to EFmwis. + // See TS 23.040 9.2.3.24.2 + + let mwi = message.mwi; + if (mwi) { + mwi.returnNumber = message.sender; + mwi.returnMessage = message.fullBody; + gMessageManager.sendVoicemailMessage("RIL:VoicemailNotification", + this.clientId, mwi); + return true; + } + + let notifyReceived = function notifyReceived(rv, domMessage) { + let success = Components.isSuccessCode(rv); + + // Acknowledge the reception of the SMS. + this.workerMessenger.send("ackSMS", { + 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 " + message.id + ", error code " + rv); + } + return; + } + + this.broadcastSmsSystemMessage("sms-received", domMessage); + Services.obs.notifyObservers(domMessage, kSmsReceivedObserverTopic, null); + }.bind(this); + + if (message.messageClass != RIL.GECKO_SMS_MESSAGE_CLASSES[RIL.PDU_DCS_MSG_CLASS_0]) { + 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 = + gMobileMessageService.createSmsMessage(message.id, + message.threadId, + message.delivery, + message.deliveryStatus, + message.sender, + message.receiver, + message.body, + message.messageClass, + message.timestamp, + message.read); + + notifyReceived(Cr.NS_OK, domMessage); + } + + // SMS ACK will be sent in notifyReceived. Return false here. + return false; + }, + /** * Handle data call state changes. */ @@ -1481,6 +2309,8 @@ RadioInterface.prototype = { } break; case "xpcom-shutdown": + // Cancel the timer for the call-ring wake lock. + this._cancelCallRingWakeLockTimer(); // Shutdown all RIL network interfaces for each (let apnSetting in this.apnSettings.byAPN) { if (apnSetting.iface) { @@ -1630,6 +2460,56 @@ RadioInterface.prototype = { // Handle phone functions of nsIRILContentHelper + enumerateCalls: function enumerateCalls(target, message) { + if (DEBUG) this.debug("Requesting enumeration of calls for callback"); + this.workerMessenger.send("enumerateCalls", message, (function(response) { + for (let call of response.calls) { + call.state = convertRILCallState(call.state); + call.isActive = this._activeCall ? + call.callIndex == this._activeCall.callIndex : false; + } + target.sendAsyncMessage("RIL:EnumerateCalls", response); + return false; + }).bind(this)); + }, + + _validateNumber: function _validateNumber(number) { + // note: isPlainPhoneNumber also accepts USSD and SS numbers + if (PhoneNumberUtils.isPlainPhoneNumber(number)) { + return true; + } + + this.handleCallError({ + callIndex: -1, + errorMsg: RIL.RIL_CALL_FAILCAUSE_TO_GECKO_CALL_ERROR[RIL.CALL_FAIL_UNOBTAINABLE_NUMBER] + }); + if (DEBUG) { + this.debug("Number '" + number + "' doesn't seem to be a viable number." + + " Drop."); + } + + return false; + }, + + dial: function dial(number) { + if (DEBUG) this.debug("Dialing " + number); + number = PhoneNumberUtils.normalize(number); + if (this._validateNumber(number)) { + this.workerMessenger.send("dial", { number: number, + isDialEmergency: false }); + } + }, + + dialEmergency: function dialEmergency(number) { + if (DEBUG) this.debug("Dialing emergency " + number); + // 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 (this._validateNumber(number)) { + this.workerMessenger.send("dial", { number: number, + isDialEmergency: true }); + } + }, + _sendCfStateChanged: function _sendCfStateChanged(message) { gMessageManager.sendMobileConnectionMessage("RIL:CfStateChanged", this.clientId, message); @@ -1684,6 +2564,654 @@ RadioInterface.prototype = { }).bind(this)); }, + get microphoneMuted() { + return gAudioManager.microphoneMuted; + }, + set microphoneMuted(value) { + if (value == this.microphoneMuted) { + return; + } + gAudioManager.microphoneMuted = value; + + if (!this._activeCall) { + gAudioManager.phoneState = nsIAudioManager.PHONE_STATE_NORMAL; + } + }, + + get speakerEnabled() { + return (gAudioManager.getForceForUse(nsIAudioManager.USE_COMMUNICATION) == + nsIAudioManager.FORCE_SPEAKER); + }, + set speakerEnabled(value) { + if (value == this.speakerEnabled) { + return; + } + let force = value ? nsIAudioManager.FORCE_SPEAKER : + nsIAudioManager.FORCE_NONE; + gAudioManager.setForceForUse(nsIAudioManager.USE_COMMUNICATION, force); + + if (!this._activeCall) { + gAudioManager.phoneState = nsIAudioManager.PHONE_STATE_NORMAL; + } + }, + + /** + * 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], + ], + + /** + * Use 16-bit reference number for concatenated outgoint messages. + * + * TODO: Support static/runtime settings, see bug 733331. + */ + segmentRef16Bit: false, + + /** + * Get valid SMS concatenation reference number. + */ + _segmentRef: 0, + get nextSegmentRef() { + let ref = this._segmentRef++; + + this._segmentRef %= (this.segmentRef16Bit ? 65535 : 255); + + // 0 is not a valid SMS concatenation reference number. + return ref + 1; + }, + + /** + * Calculate encoded length using specified locking/single shift table + * + * @param message + * message string to be encoded. + * @param langTable + * locking shift table string. + * @param langShiftTable + * single shift table string. + * @param strict7BitEncoding + * Optional. Enable Latin characters replacement with corresponding + * ones in GSM SMS 7-bit default alphabet. + * + * @return encoded length in septets. + * + * @note that the algorithm used in this function must match exactly with + * GsmPDUHelper#writeStringAsSeptets. + */ + _countGsm7BitSeptets: function _countGsm7BitSeptets(message, langTable, langShiftTable, strict7BitEncoding) { + let length = 0; + for (let msgIndex = 0; msgIndex < message.length; msgIndex++) { + let c = message.charAt(msgIndex); + if (strict7BitEncoding) { + c = RIL.GSM_SMS_STRICT_7BIT_CHARMAP[c] || c; + } + + let septet = langTable.indexOf(c); + + // According to 3GPP TS 23.038, section 6.1.1 General notes, "The + // characters marked '1)' are not used but are displayed as a space." + if (septet == RIL.PDU_NL_EXTENDED_ESCAPE) { + continue; + } + + if (septet >= 0) { + length++; + continue; + } + + 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 = '*'; + 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; + } + + continue; + } + + // According to 3GPP TS 23.038 B.2, "This code represents a control + // character and therefore must not be used for language specific + // characters." + if (septet == RIL.PDU_NL_RESERVED_CONTROL) { + continue; + } + + // The character is not found in locking shfit table, but could be + // encoded as with single shift table. Note that it's + // still possible for septet to has the value of PDU_NL_EXTENDED_ESCAPE, + // but we can display it as a space in this case as said in previous + // comment. + length += 2; + } + + return length; + }, + + /** + * Calculate user data length of specified message string encoded in GSM 7Bit + * alphabets. + * + * @param message + * a message string to be encoded. + * @param strict7BitEncoding + * Optional. Enable Latin characters replacement with corresponding + * ones in GSM SMS 7-bit default alphabet. + * + * @return null or an options object with attributes `dcs`, + * `userDataHeaderLength`, `encodedFullBodyLength`, `langIndex`, + * `langShiftIndex`, `segmentMaxSeq` set. + * + * @see #_calculateUserDataLength(). + */ + _calculateUserDataLength7Bit: function _calculateUserDataLength7Bit(message, strict7BitEncoding) { + let options = null; + let minUserDataSeptets = Number.MAX_VALUE; + for (let i = 0; i < this.enabledGsmTableTuples.length; i++) { + let [langIndex, langShiftIndex] = this.enabledGsmTableTuples[i]; + + const langTable = RIL.PDU_NL_LOCKING_SHIFT_TABLES[langIndex]; + const langShiftTable = RIL.PDU_NL_SINGLE_SHIFT_TABLES[langShiftIndex]; + + let bodySeptets = this._countGsm7BitSeptets(message, + langTable, + langShiftTable, + strict7BitEncoding); + if (bodySeptets < 0) { + continue; + } + + let headerLen = 0; + if (langIndex != RIL.PDU_NL_IDENTIFIER_DEFAULT) { + headerLen += 3; // IEI + len + langIndex + } + if (langShiftIndex != RIL.PDU_NL_IDENTIFIER_DEFAULT) { + headerLen += 3; // IEI + len + langShiftIndex + } + + // Calculate full user data length, note the extra byte is for header len + let headerSeptets = Math.ceil((headerLen ? headerLen + 1 : 0) * 8 / 7); + let segmentSeptets = RIL.PDU_MAX_USER_DATA_7BIT; + if ((bodySeptets + headerSeptets) > segmentSeptets) { + headerLen += this.segmentRef16Bit ? 6 : 5; + headerSeptets = Math.ceil((headerLen + 1) * 8 / 7); + segmentSeptets -= headerSeptets; + } + + let segments = Math.ceil(bodySeptets / segmentSeptets); + let userDataSeptets = bodySeptets + headerSeptets * segments; + if (userDataSeptets >= minUserDataSeptets) { + continue; + } + + minUserDataSeptets = userDataSeptets; + + options = { + dcs: RIL.PDU_DCS_MSG_CODING_7BITS_ALPHABET, + encodedFullBodyLength: bodySeptets, + userDataHeaderLength: headerLen, + langIndex: langIndex, + langShiftIndex: langShiftIndex, + segmentMaxSeq: segments, + segmentChars: segmentSeptets, + }; + } + + return options; + }, + + /** + * Calculate user data length of specified message string encoded in UCS2. + * + * @param message + * a message string to be encoded. + * + * @return an options object with attributes `dcs`, `userDataHeaderLength`, + * `encodedFullBodyLength`, `segmentMaxSeq` set. + * + * @see #_calculateUserDataLength(). + */ + _calculateUserDataLengthUCS2: function _calculateUserDataLengthUCS2(message) { + let bodyChars = message.length; + let headerLen = 0; + let headerChars = Math.ceil((headerLen ? headerLen + 1 : 0) / 2); + let segmentChars = RIL.PDU_MAX_USER_DATA_UCS2; + if ((bodyChars + headerChars) > segmentChars) { + headerLen += this.segmentRef16Bit ? 6 : 5; + headerChars = Math.ceil((headerLen + 1) / 2); + segmentChars -= headerChars; + } + + let segments = Math.ceil(bodyChars / segmentChars); + + return { + dcs: RIL.PDU_DCS_MSG_CODING_16BITS_ALPHABET, + encodedFullBodyLength: bodyChars * 2, + userDataHeaderLength: headerLen, + segmentMaxSeq: segments, + segmentChars: segmentChars, + }; + }, + + /** + * Calculate user data length and its encoding. + * + * @param message + * a message string to be encoded. + * @param strict7BitEncoding + * Optional. Enable Latin characters replacement with corresponding + * ones in GSM SMS 7-bit default alphabet. + * + * @return an options object with some or all of following attributes set: + * + * @param dcs + * Data coding scheme. One of the PDU_DCS_MSG_CODING_*BITS_ALPHABET + * constants. + * @param userDataHeaderLength + * Length of embedded user data header, in bytes. The whole header + * size will be userDataHeaderLength + 1; 0 for no header. + * @param encodedFullBodyLength + * Length of the message body when encoded with the given DCS. For + * UCS2, in bytes; for 7-bit, in septets. + * @param langIndex + * Table index used for normal 7-bit encoded character lookup. + * @param langShiftIndex + * Table index used for escaped 7-bit encoded character lookup. + * @param segmentMaxSeq + * Max sequence number of a multi-part messages, or 1 for single one. + * This number might not be accurate for a multi-part message until + * it's processed by #_fragmentText() again. + */ + _calculateUserDataLength: function _calculateUserDataLength(message, strict7BitEncoding) { + let options = this._calculateUserDataLength7Bit(message, strict7BitEncoding); + if (!options) { + options = this._calculateUserDataLengthUCS2(message); + } + + if (DEBUG) this.debug("_calculateUserDataLength: " + JSON.stringify(options)); + return options; + }, + + /** + * Fragment GSM 7-Bit encodable string for transmission. + * + * @param text + * text string to be fragmented. + * @param langTable + * locking shift table string. + * @param langShiftTable + * single shift table string. + * @param segmentSeptets + * Number of available spetets per segment. + * @param strict7BitEncoding + * Optional. Enable Latin characters replacement with corresponding + * ones in GSM SMS 7-bit default alphabet. + * + * @return an array of objects. See #_fragmentText() for detailed definition. + */ + _fragmentText7Bit: function _fragmentText7Bit(text, langTable, langShiftTable, segmentSeptets, strict7BitEncoding) { + let ret = []; + let body = "", len = 0; + for (let i = 0, inc = 0; i < text.length; i++) { + let c = text.charAt(i); + if (strict7BitEncoding) { + c = RIL.GSM_SMS_STRICT_7BIT_CHARMAP[c] || c; + } + + let septet = langTable.indexOf(c); + if (septet == RIL.PDU_NL_EXTENDED_ESCAPE) { + continue; + } + + if (septet >= 0) { + inc = 1; + } else { + septet = langShiftTable.indexOf(c); + if (septet == RIL.PDU_NL_RESERVED_CONTROL) { + continue; + } + + 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 = '*'; + if (langTable.indexOf(c) >= 0) { + inc = 1; + } + } + } + + if ((len + inc) > segmentSeptets) { + ret.push({ + body: body, + encodedBodyLength: len, + }); + body = c; + len = inc; + } else { + body += c; + len += inc; + } + } + + if (len) { + ret.push({ + body: body, + encodedBodyLength: len, + }); + } + + return ret; + }, + + /** + * Fragment UCS2 encodable string for transmission. + * + * @param text + * text string to be fragmented. + * @param segmentChars + * Number of available characters per segment. + * + * @return an array of objects. See #_fragmentText() for detailed definition. + */ + _fragmentTextUCS2: function _fragmentTextUCS2(text, segmentChars) { + let ret = []; + for (let offset = 0; offset < text.length; offset += segmentChars) { + let str = text.substr(offset, segmentChars); + ret.push({ + body: str, + encodedBodyLength: str.length * 2, + }); + } + + return ret; + }, + + /** + * Fragment string for transmission. + * + * Fragment input text string into an array of objects that contains + * attributes `body`, substring for this segment, `encodedBodyLength`, + * length of the encoded segment body in septets. + * + * @param text + * Text string to be fragmented. + * @param options + * Optional pre-calculated option object. The output array will be + * stored at options.segments if there are multiple segments. + * @param strict7BitEncoding + * Optional. Enable Latin characters replacement with corresponding + * ones in GSM SMS 7-bit default alphabet. + * + * @return Populated options object. + */ + _fragmentText: function _fragmentText(text, options, strict7BitEncoding) { + if (!options) { + options = this._calculateUserDataLength(text, strict7BitEncoding); + } + + if (options.dcs == RIL.PDU_DCS_MSG_CODING_7BITS_ALPHABET) { + const langTable = RIL.PDU_NL_LOCKING_SHIFT_TABLES[options.langIndex]; + const langShiftTable = RIL.PDU_NL_SINGLE_SHIFT_TABLES[options.langShiftIndex]; + options.segments = this._fragmentText7Bit(text, + langTable, langShiftTable, + options.segmentChars, + strict7BitEncoding); + } else { + options.segments = this._fragmentTextUCS2(text, + options.segmentChars); + } + + // Re-sync options.segmentMaxSeq with actual length of returning array. + options.segmentMaxSeq = options.segments.length; + + return options; + }, + + getSegmentInfoForText: function getSegmentInfoForText(text, request) { + let strict7BitEncoding; + try { + strict7BitEncoding = Services.prefs.getBoolPref("dom.sms.strict7BitEncoding"); + } catch (e) { + strict7BitEncoding = false; + } + + let options = this._fragmentText(text, null, strict7BitEncoding); + let charsInLastSegment; + if (options.segmentMaxSeq) { + let lastSegment = options.segments[options.segmentMaxSeq - 1]; + charsInLastSegment = lastSegment.encodedBodyLength; + if (options.dcs == RIL.PDU_DCS_MSG_CODING_16BITS_ALPHABET) { + // In UCS2 encoding, encodedBodyLength is in octets. + charsInLastSegment /= 2; + } + } else { + charsInLastSegment = 0; + } + + let result = gMobileMessageService + .createSmsSegmentInfo(options.segmentMaxSeq, + options.segmentChars, + options.segmentChars - charsInLastSegment); + request.notifySegmentInfoForTextGot(result); + }, + + sendSMS: function sendSMS(number, message, silent, request) { + let strict7BitEncoding; + try { + strict7BitEncoding = Services.prefs.getBoolPref("dom.sms.strict7BitEncoding"); + } catch (e) { + strict7BitEncoding = false; + } + + let options = this._fragmentText(message, null, strict7BitEncoding); + options.number = PhoneNumberUtils.normalize(number); + let requestStatusReport; + try { + requestStatusReport = + Services.prefs.getBoolPref("dom.sms.requestStatusReport"); + } catch (e) { + requestStatusReport = true; + } + options.requestStatusReport = requestStatusReport && !silent; + if (options.segmentMaxSeq > 1) { + options.segmentRef16Bit = this.segmentRef16Bit; + options.segmentRef = this.nextSegmentRef; + } + + let notifyResult = (function notifyResult(rv, domMessage) { + // TODO bug 832140 handle !Components.isSuccessCode(rv) + if (!silent) { + Services.obs.notifyObservers(domMessage, kSmsSendingObserverTopic, null); + } + + // 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) { + 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) { + request.notifySendMessageFailed(errorCode); + return; + } + + gMobileMessageDatabaseService + .setMessageDeliveryByMessageId(domMessage.id, + null, + DOM_MOBILE_MESSAGE_DELIVERY_ERROR, + RIL.GECKO_SMS_DELIVERY_STATUS_ERROR, + null, + function notifyResult(rv, domMessage) { + // TODO bug 832140 handle !Components.isSuccessCode(rv) + request.notifySendMessageFailed(errorCode); + Services.obs.notifyObservers(domMessage, kSmsFailedObserverTopic, null); + }); + return; + } + + // Keep current SMS message info for sent/delivered notifications + let context = { + request: request, + sms: domMessage, + requestStatusReport: options.requestStatusReport, + silent: silent + }; + + // This is the entry point starting to send SMS. + this.workerMessenger.send("sendSMS", options, + (function(context, response) { + if (response.errorMsg) { + // Failed to send SMS out. + let error = Ci.nsIMobileMessageCallback.UNKNOWN_ERROR; + switch (response.errorMsg) { + case RIL.ERROR_RADIO_NOT_AVAILABLE: + error = Ci.nsIMobileMessageCallback.NO_SIGNAL_ERROR; + break; + } + + if (context.silent) { + context.request.notifySendMessageFailed(error); + return false; + } + + gMobileMessageDatabaseService + .setMessageDeliveryByMessageId(context.sms.id, + null, + DOM_MOBILE_MESSAGE_DELIVERY_ERROR, + RIL.GECKO_SMS_DELIVERY_STATUS_ERROR, + null, + function notifyResult(rv, domMessage) { + // TODO bug 832140 handle !Components.isSuccessCode(rv) + context.request.notifySendMessageFailed(error); + Services.obs.notifyObservers(domMessage, kSmsFailedObserverTopic, null); + }); + return false; + } // End of send failure. + + if (response.deliveryStatus) { + // Message delivery. + gMobileMessageDatabaseService + .setMessageDeliveryByMessageId(context.sms.id, + null, + context.sms.delivery, + response.deliveryStatus, + null, + function notifyResult(rv, domMessage) { + // TODO bug 832140 handle !Components.isSuccessCode(rv) + let topic = (response.deliveryStatus == RIL.GECKO_SMS_DELIVERY_STATUS_SUCCESS) + ? kSmsDeliverySuccessObserverTopic + : kSmsDeliveryErrorObserverTopic; + Services.obs.notifyObservers(domMessage, topic, null); + }); + + // Send transaction has ended completely. + return false; + } // End of message delivery. + + // Message sent. + if (context.silent) { + // There is no way to modify nsIDOMMozSmsMessage attributes as they are + // read only so we just create a new sms instance to send along with + // the notification. + let sms = context.sms; + context.request.notifyMessageSent( + gMobileMessageService.createSmsMessage(sms.id, + sms.threadId, + DOM_MOBILE_MESSAGE_DELIVERY_SENT, + sms.deliveryStatus, + sms.sender, + sms.receiver, + sms.body, + sms.messageClass, + sms.timestamp, + sms.read)); + // We don't wait for SMS-DELIVER-REPORT for silent one. + return false; + } + + gMobileMessageDatabaseService + .setMessageDeliveryByMessageId(context.sms.id, + null, + DOM_MOBILE_MESSAGE_DELIVERY_SENT, + context.sms.deliveryStatus, + null, + (function notifyResult(rv, domMessage) { + // TODO bug 832140 handle !Components.isSuccessCode(rv) + this.broadcastSmsSystemMessage("sms-sent", domMessage); + + if (context.requestStatusReport) { + context.sms = domMessage; + } + + context.request.notifyMessageSent(domMessage); + Services.obs.notifyObservers(domMessage, kSmsSentObserverTopic, null); + }).bind(this)); + + // Only keep current context if we have requested for delivery report. + return context.requestStatusReport; + }).bind(this, context)); // End of |workerMessenger.send| callback. + }).bind(this); // End of DB saveSendingMessage callback. + + let sendingMessage = { + type: "sms", + sender: this.getMsisdn(), + receiver: number, + body: message, + deliveryStatusRequested: options.requestStatusReport, + timestamp: Date.now() + }; + + if (silent) { + let deliveryStatus = RIL.GECKO_SMS_DELIVERY_STATUS_PENDING; + let delivery = DOM_MOBILE_MESSAGE_DELIVERY_SENDING; + let domMessage = + gMobileMessageService.createSmsMessage(-1, // id + 0, // threadId + delivery, + deliveryStatus, + sendingMessage.sender, + sendingMessage.receiver, + sendingMessage.body, + "normal", // message class + sendingMessage.timestamp, + false); + notifyResult(Cr.NS_OK, domMessage); + return; + } + + let id = gMobileMessageDatabaseService.saveSendingMessage( + sendingMessage, notifyResult); + }, + registerDataCallCallback: function registerDataCallCallback(callback) { if (this._datacall_callbacks) { if (this._datacall_callbacks.indexOf(callback) != -1) { @@ -1834,13 +3362,6 @@ RadioInterface.prototype = { this.workerMessenger.send("deactivateDataCall", { cid: cid, reason: reason }); }, - - sendWorkerMessage: function sendWorkerMessage(rilMessageType, message, - callback) { - this.workerMessenger.send(rilMessageType, message, function (response) { - return callback.handleResponse(response); - }); - } }; function RILNetworkInterface(radioInterface, apnSetting) { diff --git a/dom/system/gonk/RilMessageManager.jsm b/dom/system/gonk/RilMessageManager.jsm deleted file mode 100644 index fece7a68fc30..000000000000 --- a/dom/system/gonk/RilMessageManager.jsm +++ /dev/null @@ -1,330 +0,0 @@ -/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ -/* 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/XPCOMUtils.jsm"); -Cu.import("resource://gre/modules/Services.jsm"); - -XPCOMUtils.defineLazyServiceGetter(this, "ppmm", - "@mozilla.org/parentprocessmessagemanager;1", - "nsIMessageBroadcaster"); - -// Observer topics. -const kPrefenceChangedObserverTopic = "nsPref:changed"; -const kSysMsgListenerReadyObserverTopic = "system-message-listener-ready"; -const kXpcomShutdownObserverTopic = "xpcom-shutdown"; - -// Preference keys. -const kPrefKeyRilDebuggingEnabled = "ril.debugging.enabled"; - -// Frame message names. -const kMsgNameChildProcessShutdown = "child-process-shutdown"; - -let DEBUG; -function debug(s) { - dump("RilMessageManager: " + s + "\n"); -} - -this.RilMessageManager = { - QueryInterface: XPCOMUtils.generateQI([Ci.nsIMessageListener, - Ci.nsIObserver]), - - topicRegistrationNames: { - cellbroadcast: "RIL:RegisterCellBroadcastMsg", - icc: "RIL:RegisterIccMsg", - mobileconnection: "RIL:RegisterMobileConnectionMsg", - voicemail: "RIL:RegisterVoicemailMsg", - }, - - /** - * this.callbacksByName[< A string message name>] = { - * topic: , - * callback: , - * } - */ - callbacksByName: {}, - - // Manage message targets in terms of topic. Only the authorized and - // registered contents can receive related messages. - targetsByTopic: {}, - topics: [], - - targetMessageQueue: [], - ready: false, - - _init: function _init() { - this._updateDebugFlag(); - - Services.obs.addObserver(this, kPrefenceChangedObserverTopic, false); - Services.obs.addObserver(this, kSysMsgListenerReadyObserverTopic, false); - Services.obs.addObserver(this, kXpcomShutdownObserverTopic, false); - - ppmm.addMessageListener(kMsgNameChildProcessShutdown, this); - - let callback = this._registerMessageTarget.bind(this); - for (let topic in this.topicRegistrationNames) { - let name = this.topicRegistrationNames[topic]; - this.registerMessageListeners(topic, [name], callback); - } - }, - - _shutdown: function _shutdown() { - if (!this.ready) { - Services.obs.removeObserver(this, kSysMsgListenerReadyObserverTopic); - } - Services.obs.removeObserver(this, kPrefenceChangedObserverTopic); - Services.obs.removeObserver(this, kXpcomShutdownObserverTopic); - - for (let name in this.callbacksByName) { - ppmm.removeMessageListener(name, this); - } - this.callbacksByName = null; - - ppmm.removeMessageListener(kMsgNameChildProcessShutdown, this); - ppmm = null; - - this.targetsByTopic = null; - this.targetMessageQueue = null; - }, - - _registerMessageTarget: function _registerMessageTarget(topic, msg) { - let targets = this.targetsByTopic[topic]; - if (!targets) { - targets = this.targetsByTopic[topic] = []; - let list = this.topics; - if (list.indexOf(topic) == -1) { - list.push(topic); - } - } - - let target = msg.target; - if (targets.indexOf(target) != -1) { - if (DEBUG) debug("Already registered this target!"); - return; - } - - targets.push(target); - if (DEBUG) debug("Registered " + topic + " target: " + target); - }, - - _unregisterMessageTarget: function _unregisterMessageTarget(topic, target) { - if (topic == null) { - // Unregister the target for every topic when no topic is specified. - for (let type of this.topics) { - this._unregisterMessageTarget(type, target); - } - return; - } - - // Unregister the target for a specified topic. - let targets = this.targetsByTopic[topic]; - if (!targets) { - return; - } - - let index = targets.indexOf(target); - if (index != -1) { - targets.splice(index, 1); - if (DEBUG) debug("Unregistered " + topic + " target: " + target); - } - }, - - _enqueueTargetMessage: function _enqueueTargetMessage(topic, name, options) { - let msg = { topic : topic, - name : name, - options : options }; - // Remove previous queued message of same message name, only one message - // per message name is allowed in queue. - let messageQueue = this.targetMessageQueue; - for (let i = 0; i < messageQueue.length; i++) { - if (messageQueue[i].name === name) { - messageQueue.splice(i, 1); - break; - } - } - - messageQueue.push(msg); - }, - - _sendTargetMessage: function _sendTargetMessage(topic, name, options) { - if (!this.ready) { - this._enqueueTargetMessage(topic, name, options); - return; - } - - let targets = this.targetsByTopic[topic]; - if (!targets) { - return; - } - - for (let target of targets) { - target.sendAsyncMessage(name, options); - } - }, - - _resendQueuedTargetMessage: function _resendQueuedTargetMessage() { - // Here uses this._sendTargetMessage() to resend message, which will - // enqueue message if listener is not ready. So only resend after listener - // is ready, or it will cause infinate loop and hang the system. - - // Dequeue and resend messages. - for (let msg of this.targetMessageQueue) { - this._sendTargetMessage(msg.topic, msg.name, msg.options); - } - this.targetMessageQueue = null; - }, - - _updateDebugFlag: function _updateDebugFlag() { - try { - DEBUG = RIL.DEBUG_RIL || - Services.prefs.getBoolPref(kPrefKeyRilDebuggingEnabled); - } catch(e) {} - }, - - /** - * nsIMessageListener interface methods. - */ - - receiveMessage: function receiveMessage(msg) { - if (DEBUG) { - debug("Received '" + msg.name + "' message from content process"); - } - - if (msg.name == kMsgNameChildProcessShutdown) { - // By the time we receive child-process-shutdown, the child process has - // already forgotten its permissions so we need to unregister the target - // for every permission. - this._unregisterMessageTarget(null, msg.target); - return; - } - - let entry = this.callbacksByName[msg.name]; - if (!entry) { - if (DEBUG) debug("Ignoring unknown message type: " + msg.name); - return null; - } - - if (entry.topic && !msg.target.assertPermission(entry.topic)) { - if (DEBUG) { - debug("Message " + msg.name + " from a content process with no '" + - entry.topic + "' privileges."); - } - return null; - } - - return entry.callback(entry.topic, msg); - }, - - /** - * nsIObserver interface methods. - */ - - observe: function observe(subject, topic, data) { - switch (topic) { - case kSysMsgListenerReadyObserverTopic: - this.ready = true; - Services.obs.removeObserver(this, kSysMsgListenerReadyObserverTopic); - - this._resendQueuedTargetMessage(); - break; - - case kPrefenceChangedObserverTopic: - if (data === kPrefKeyRilDebuggingEnabled) { - this._updateDebugFlag(); - } - break; - - case kXpcomShutdownObserverTopic: - this._shutdown(); - break; - } - }, - - /** - * Public methods. - */ - - /** - * @param topic - * A string for the topic of the registrating names. Also the - * permission to listen messages of these names. - * @param names - * An array of string message names to listen. - * @param callback - * A callback that accepts two parameters -- topic and msg. - */ - registerMessageListeners: function registerMessageListeners(topic, names, - callback) { - for (let name of names) { - if (this.callbacksByName[name]) { - if (DEBUG) { - debug("Message name '" + name + "' was already registered. Ignored."); - } - continue; - } - - this.callbacksByName[name] = { topic: topic, callback: callback }; - ppmm.addMessageListener(name, this); - } - }, - - /** - * Remove all listening names with specified callback. - * - * @param callback - * The callback previously registered for messages. - */ - unregisterMessageListeners: function unregisterMessageListeners(callback) { - let remains = {}; - for (let name in this.callbacksByName) { - let entry = this.callbacksByName[name]; - if (entry.callback != callback) { - remains[name] = entry; - } else { - ppmm.removeMessageListener(name, this); - } - } - this.callbacksByName = remains; - }, - - sendMobileConnectionMessage: function sendMobileConnectionMessage(name, - clientId, - data) { - this._sendTargetMessage("mobileconnection", name, { - clientId: clientId, - data: data - }); - }, - - sendVoicemailMessage: function sendVoicemailMessage(name, clientId, data) { - this._sendTargetMessage("voicemail", name, { - clientId: clientId, - data: data - }); - }, - - sendCellBroadcastMessage: function sendCellBroadcastMessage(name, clientId, - data) { - this._sendTargetMessage("cellbroadcast", name, { - clientId: clientId, - data: data - }); - }, - - sendIccMessage: function sendIccMessage(name, clientId, data) { - this._sendTargetMessage("icc", name, { - clientId: clientId, - data: data - }); - } -}; - -RilMessageManager._init(); - -this.EXPORTED_SYMBOLS = ["RilMessageManager"]; diff --git a/dom/system/gonk/moz.build b/dom/system/gonk/moz.build index e4d53da7f572..694b01a02177 100644 --- a/dom/system/gonk/moz.build +++ b/dom/system/gonk/moz.build @@ -74,7 +74,6 @@ EXTRA_COMPONENTS += [ ] EXTRA_JS_MODULES += [ - 'RilMessageManager.jsm', 'net_worker.js', 'ril_consts.js', 'ril_worker.js', diff --git a/dom/system/gonk/nsIRadioInterfaceLayer.idl b/dom/system/gonk/nsIRadioInterfaceLayer.idl index 770c1e1acf77..71afdbcf8f5c 100644 --- a/dom/system/gonk/nsIRadioInterfaceLayer.idl +++ b/dom/system/gonk/nsIRadioInterfaceLayer.idl @@ -6,6 +6,7 @@ interface nsIDOMMozIccInfo; interface nsIDOMMozMobileConnectionInfo; +interface nsIMobileMessageCallback; [scriptable, uuid(1e602d20-d066-4399-8997-daf36b3158ef)] interface nsIRILDataCallInfo : nsISupports @@ -77,13 +78,7 @@ interface nsIRilContext : nsISupports readonly attribute nsIDOMMozMobileConnectionInfo data; }; -[scriptable, function, uuid(3bc96351-53b0-47a1-a888-c74c64b60f25)] -interface nsIRilSendWorkerMessageCallback : nsISupports -{ - boolean handleResponse(in jsval response); -}; - -[scriptable, uuid(b1af7aad-6547-427c-a878-e2ebf98a14d6)] +[scriptable, uuid(a50d65aa-00da-11e3-b954-7bfb233d98fc)] interface nsIRadioInterface : nsISupports { readonly attribute nsIRilContext rilContext; @@ -100,9 +95,16 @@ interface nsIRadioInterface : nsISupports void updateRILNetworkInterface(); - void sendWorkerMessage(in DOMString type, - [optional] in jsval message, - [optional] in nsIRilSendWorkerMessageCallback callback); + /** + * SMS-related functionality. + */ + void getSegmentInfoForText(in DOMString text, + in nsIMobileMessageCallback request); + + void sendSMS(in DOMString number, + in DOMString message, + in boolean silent, + in nsIMobileMessageCallback request); }; [scriptable, uuid(44b03951-1444-4c03-bd37-0bcb3a01b56f)] diff --git a/dom/telephony/CallEvent.cpp b/dom/telephony/CallEvent.cpp index 580750f06089..30ab81aee468 100644 --- a/dom/telephony/CallEvent.cpp +++ b/dom/telephony/CallEvent.cpp @@ -11,7 +11,6 @@ USING_TELEPHONY_NAMESPACE using namespace mozilla::dom; -using mozilla::ErrorResult; /* static */ already_AddRefed diff --git a/dom/telephony/Makefile.in b/dom/telephony/Makefile.in index ba0eb3e44d09..e2f1f0e33732 100644 --- a/dom/telephony/Makefile.in +++ b/dom/telephony/Makefile.in @@ -3,5 +3,3 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. include $(topsrcdir)/dom/dom-config.mk -include $(topsrcdir)/config/rules.mk -include $(topsrcdir)/ipc/chromium/chromium-config.mk diff --git a/dom/telephony/Telephony.cpp b/dom/telephony/Telephony.cpp index 95749b0fb6f5..430242e65c6a 100644 --- a/dom/telephony/Telephony.cpp +++ b/dom/telephony/Telephony.cpp @@ -24,9 +24,10 @@ #include "TelephonyCall.h" #include "TelephonyCallGroup.h" +#define NS_RILCONTENTHELPER_CONTRACTID "@mozilla.org/ril/content-helper;1" + USING_TELEPHONY_NAMESPACE using namespace mozilla::dom; -using mozilla::ErrorResult; namespace { @@ -50,8 +51,6 @@ public: MOZ_ASSERT(mTelephony); } - virtual ~Listener() {} - void Disconnect() { @@ -112,7 +111,7 @@ Telephony::Shutdown() mListener->Disconnect(); if (mProvider) { - mProvider->UnregisterListener(mListener); + mProvider->UnregisterTelephonyMsg(mListener); mProvider = nullptr; } @@ -133,7 +132,7 @@ Telephony::Create(nsPIDOMWindow* aOwner, ErrorResult& aRv) NS_ASSERTION(aOwner, "Null owner!"); nsCOMPtr ril = - do_GetService(TELEPHONY_PROVIDER_CONTRACTID); + do_GetService(NS_RILCONTENTHELPER_CONTRACTID); if (!ril) { aRv.Throw(NS_ERROR_UNEXPECTED); return nullptr; @@ -166,7 +165,7 @@ Telephony::Create(nsPIDOMWindow* aOwner, ErrorResult& aRv) return nullptr; } - rv = ril->RegisterListener(telephony->mListener); + rv = ril->RegisterTelephonyMsg(telephony->mListener); if (NS_FAILED(rv)) { aRv.Throw(rv); return nullptr; @@ -223,7 +222,12 @@ Telephony::DialInternal(bool isEmergency, } } - nsresult rv = mProvider->Dial(aNumber, isEmergency); + nsresult rv; + if (isEmergency) { + rv = mProvider->DialEmergency(aNumber); + } else { + rv = mProvider->Dial(aNumber); + } if (NS_FAILED(rv)) { aRv.Throw(rv); return nullptr; @@ -587,7 +591,7 @@ NS_IMETHODIMP Telephony::EnumerateCallState(uint32_t aCallIndex, uint16_t aCallState, const nsAString& aNumber, bool aIsActive, bool aIsOutgoing, bool aIsEmergency, - bool aIsConference) + bool aIsConference, bool* aContinue) { nsRefPtr call; @@ -599,10 +603,12 @@ Telephony::EnumerateCallState(uint32_t aCallIndex, uint16_t aCallState, call = aIsConference ? mGroup->GetCall(aCallIndex) : GetCall(aCallIndex); if (call) { // We have the call either in mCalls or in mGroup. Skip it. + *aContinue = true; return NS_OK; } if (MoveCall(aCallIndex, aIsConference)) { + *aContinue = true; return NS_OK; } @@ -616,6 +622,7 @@ Telephony::EnumerateCallState(uint32_t aCallIndex, uint16_t aCallState, mCalls.Contains(call), "Should have auto-added new call!"); + *aContinue = true; return NS_OK; } diff --git a/dom/telephony/TelephonyCall.cpp b/dom/telephony/TelephonyCall.cpp index 23868f829641..f90389da17a9 100644 --- a/dom/telephony/TelephonyCall.cpp +++ b/dom/telephony/TelephonyCall.cpp @@ -15,7 +15,6 @@ USING_TELEPHONY_NAMESPACE using namespace mozilla::dom; -using mozilla::ErrorResult; // static already_AddRefed diff --git a/dom/telephony/TelephonyCallGroup.cpp b/dom/telephony/TelephonyCallGroup.cpp index d5571a657ac3..0e71643afba1 100644 --- a/dom/telephony/TelephonyCallGroup.cpp +++ b/dom/telephony/TelephonyCallGroup.cpp @@ -13,7 +13,6 @@ USING_TELEPHONY_NAMESPACE using namespace mozilla::dom; -using mozilla::ErrorResult; TelephonyCallGroup::TelephonyCallGroup() : mCallState(nsITelephonyProvider::CALL_STATE_UNKNOWN) diff --git a/dom/telephony/TelephonyFactory.cpp b/dom/telephony/TelephonyFactory.cpp deleted file mode 100644 index eab2c699185c..000000000000 --- a/dom/telephony/TelephonyFactory.cpp +++ /dev/null @@ -1,30 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 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/. */ - -#include "mozilla/dom/telephony/TelephonyFactory.h" -#ifdef MOZ_WIDGET_GONK -#include "nsIGonkTelephonyProvider.h" -#endif -#include "nsServiceManagerUtils.h" -#include "nsXULAppAPI.h" -#include "ipc/TelephonyIPCProvider.h" - -USING_TELEPHONY_NAMESPACE - -/* static */ already_AddRefed -TelephonyFactory::CreateTelephonyProvider() -{ - nsCOMPtr provider; - - if (XRE_GetProcessType() == GeckoProcessType_Content) { - provider = new TelephonyIPCProvider(); -#ifdef MOZ_WIDGET_GONK - } else { - provider = do_CreateInstance(GONK_TELEPHONY_PROVIDER_CONTRACTID); -#endif - } - - return provider.forget(); -} diff --git a/dom/telephony/TelephonyFactory.h b/dom/telephony/TelephonyFactory.h deleted file mode 100644 index 07e3f779304e..000000000000 --- a/dom/telephony/TelephonyFactory.h +++ /dev/null @@ -1,24 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 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/. */ - -#ifndef mozilla_dom_telephony_TelephonyFactory_h -#define mozilla_dom_telephony_TelephonyFactory_h - -#include "nsCOMPtr.h" -#include "mozilla/dom/telephony/TelephonyCommon.h" - -class nsITelephonyProvider; - -BEGIN_TELEPHONY_NAMESPACE - -class TelephonyFactory -{ -public: - static already_AddRefed CreateTelephonyProvider(); -}; - -END_TELEPHONY_NAMESPACE - -#endif // mozilla_dom_telephony_TelephonyFactory_h diff --git a/dom/telephony/gonk/TelephonyProvider.js b/dom/telephony/gonk/TelephonyProvider.js deleted file mode 100644 index 0629f5cdea51..000000000000 --- a/dom/telephony/gonk/TelephonyProvider.js +++ /dev/null @@ -1,525 +0,0 @@ -/* -*- Mode: js; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 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/XPCOMUtils.jsm"); -Cu.import("resource://gre/modules/Services.jsm"); - -var RIL = {}; -Cu.import("resource://gre/modules/ril_consts.js", RIL); - -const GONK_TELEPHONYPROVIDER_CONTRACTID = - "@mozilla.org/telephony/gonktelephonyprovider;1"; -const GONK_TELEPHONYPROVIDER_CID = - Components.ID("{67d26434-d063-4d28-9f48-5b3189788155}"); - -const kPrefenceChangedObserverTopic = "nsPref:changed"; -const kXpcomShutdownObserverTopic = "xpcom-shutdown"; - -const nsIAudioManager = Ci.nsIAudioManager; -const nsITelephonyProvider = Ci.nsITelephonyProvider; - -const CALL_WAKELOCK_TIMEOUT = 5000; - -let DEBUG; -function debug(s) { - dump("TelephonyProvider: " + s + "\n"); -} - -XPCOMUtils.defineLazyGetter(this, "gAudioManager", function getAudioManager() { - try { - return Cc["@mozilla.org/telephony/audiomanager;1"] - .getService(nsIAudioManager); - } catch (ex) { - //TODO on the phone this should not fall back as silently. - - // Fake nsIAudioManager implementation so that we can run the telephony - // code in a non-Gonk build. - if (DEBUG) debug("Using fake audio manager."); - return { - microphoneMuted: false, - masterVolume: 1.0, - masterMuted: false, - phoneState: nsIAudioManager.PHONE_STATE_CURRENT, - _forceForUse: {}, - - setForceForUse: function setForceForUse(usage, force) { - this._forceForUse[usage] = force; - }, - - getForceForUse: function setForceForUse(usage) { - return this._forceForUse[usage] || nsIAudioManager.FORCE_NONE; - } - }; - } -}); - -XPCOMUtils.defineLazyServiceGetter(this, "gPowerManagerService", - "@mozilla.org/power/powermanagerservice;1", - "nsIPowerManagerService"); - -XPCOMUtils.defineLazyServiceGetter(this, "gSystemMessenger", - "@mozilla.org/system-message-internal;1", - "nsISystemMessagesInternal"); - -XPCOMUtils.defineLazyGetter(this, "gRadioInterface", function () { - let ril = Cc["@mozilla.org/ril;1"].getService(Ci["nsIRadioInterfaceLayer"]); - // TODO: Bug 854326 - B2G Multi-SIM: support multiple SIM cards for SMS/MMS - return ril.getRadioInterface(0); -}); - -XPCOMUtils.defineLazyGetter(this, "gPhoneNumberUtils", function () { - let ns = {}; - Cu.import("resource://gre/modules/PhoneNumberUtils.jsm", ns); - return ns.PhoneNumberUtils; -}); - -function TelephonyProvider() { - this._listeners = []; - - this._updateDebugFlag(); - - Services.obs.addObserver(this, kPrefenceChangedObserverTopic, false); - Services.obs.addObserver(this, kXpcomShutdownObserverTopic, false); -} -TelephonyProvider.prototype = { - classID: GONK_TELEPHONYPROVIDER_CID, - classInfo: XPCOMUtils.generateCI({classID: GONK_TELEPHONYPROVIDER_CID, - contractID: GONK_TELEPHONYPROVIDER_CONTRACTID, - classDescription: "TelephonyProvider", - interfaces: [Ci.nsITelephonyProvider, - Ci.nsIGonkTelephonyProvider], - flags: Ci.nsIClassInfo.SINGLETON}), - QueryInterface: XPCOMUtils.generateQI([Ci.nsITelephonyProvider, - Ci.nsIGonkTelephonyProvider, - Ci.nsIObserver]), - - _callRingWakeLock: null, - _callRingWakeLockTimer: null, - _cancelCallRingWakeLockTimer: function _cancelCallRingWakeLockTimer() { - if (this._callRingWakeLockTimer) { - this._callRingWakeLockTimer.cancel(); - } - if (this._callRingWakeLock) { - this._callRingWakeLock.unlock(); - this._callRingWakeLock = null; - } - }, - - // An array of nsITelephonyListener instances. - _listeners: null, - _notifyAllListeners: function _notifyAllListeners(aMethodName, aArgs) { - let listeners = this._listeners.slice(); - for (let listener of listeners) { - if (this._listeners.indexOf(listener) == -1) { - // Listener has been unregistered in previous run. - continue; - } - - let handler = listener[aMethodName]; - try { - handler.apply(listener, aArgs); - } catch (e) { - debug("listener for " + aMethodName + " threw an exception: " + e); - } - } - }, - - /** - * Track the active call and update the audio system as its state changes. - */ - _activeCall: null, - _updateCallAudioState: function _updateCallAudioState(aCall, - aConferenceState) { - if (aConferenceState === nsITelephonyProvider.CALL_STATE_CONNECTED) { - gAudioManager.phoneState = nsIAudioManager.PHONE_STATE_IN_CALL; - if (this.speakerEnabled) { - gAudioManager.setForceForUse(nsIAudioManager.USE_COMMUNICATION, - nsIAudioManager.FORCE_SPEAKER); - } - return; - } - if (aConferenceState === nsITelephonyProvider.CALL_STATE_UNKNOWN || - aConferenceState === nsITelephonyProvider.CALL_STATE_HELD) { - if (!this._activeCall) { - gAudioManager.phoneState = nsIAudioManager.PHONE_STATE_NORMAL; - } - return; - } - - if (!aCall) { - return; - } - - if (aCall.isConference) { - if (this._activeCall && this._activeCall.callIndex == aCall.callIndex) { - this._activeCall = null; - } - return; - } - - switch (aCall.state) { - case nsITelephonyProvider.CALL_STATE_DIALING: // Fall through... - case nsITelephonyProvider.CALL_STATE_ALERTING: - case nsITelephonyProvider.CALL_STATE_CONNECTED: - aCall.isActive = true; - this._activeCall = aCall; - gAudioManager.phoneState = nsIAudioManager.PHONE_STATE_IN_CALL; - if (this.speakerEnabled) { - gAudioManager.setForceForUse(nsIAudioManager.USE_COMMUNICATION, - nsIAudioManager.FORCE_SPEAKER); - } - if (DEBUG) { - debug("Active call, put audio system into PHONE_STATE_IN_CALL: " + - gAudioManager.phoneState); - } - break; - - case nsITelephonyProvider.CALL_STATE_INCOMING: - aCall.isActive = false; - if (!this._activeCall) { - // We can change the phone state into RINGTONE only when there's - // no active call. - gAudioManager.phoneState = nsIAudioManager.PHONE_STATE_RINGTONE; - if (DEBUG) { - debug("Incoming call, put audio system into PHONE_STATE_RINGTONE: " + - gAudioManager.phoneState); - } - } - break; - - case nsITelephonyProvider.CALL_STATE_HELD: // Fall through... - case nsITelephonyProvider.CALL_STATE_DISCONNECTED: - aCall.isActive = false; - if (this._activeCall && - this._activeCall.callIndex == aCall.callIndex) { - // Previously active call is not active now. - this._activeCall = null; - } - - if (!this._activeCall) { - // No active call. Disable the audio. - gAudioManager.phoneState = nsIAudioManager.PHONE_STATE_NORMAL; - if (DEBUG) { - debug("No active call, put audio system into PHONE_STATE_NORMAL: " + - gAudioManager.phoneState); - } - } - break; - } - }, - - _convertRILCallState: function _convertRILCallState(aState) { - switch (aState) { - case RIL.CALL_STATE_ACTIVE: - return nsITelephonyProvider.CALL_STATE_CONNECTED; - case RIL.CALL_STATE_HOLDING: - return nsITelephonyProvider.CALL_STATE_HELD; - case RIL.CALL_STATE_DIALING: - return nsITelephonyProvider.CALL_STATE_DIALING; - case RIL.CALL_STATE_ALERTING: - return nsITelephonyProvider.CALL_STATE_ALERTING; - case RIL.CALL_STATE_INCOMING: - case RIL.CALL_STATE_WAITING: - return nsITelephonyProvider.CALL_STATE_INCOMING; - default: - throw new Error("Unknown rilCallState: " + aState); - } - }, - - _convertRILSuppSvcNotification: function _convertRILSuppSvcNotification(aNotification) { - switch (aNotification) { - case RIL.GECKO_SUPP_SVC_NOTIFICATION_REMOTE_HELD: - return nsITelephonyProvider.NOTIFICATION_REMOTE_HELD; - case RIL.GECKO_SUPP_SVC_NOTIFICATION_REMOTE_RESUMED: - return nsITelephonyProvider.NOTIFICATION_REMOTE_RESUMED; - default: - throw new Error("Unknown rilSuppSvcNotification: " + aNotification); - } - }, - - _validateNumber: function _validateNumber(aNumber) { - // note: isPlainPhoneNumber also accepts USSD and SS numbers - if (gPhoneNumberUtils.isPlainPhoneNumber(aNumber)) { - return true; - } - - let errorMsg = RIL.RIL_CALL_FAILCAUSE_TO_GECKO_CALL_ERROR[RIL.CALL_FAIL_UNOBTAINABLE_NUMBER]; - let currentThread = Services.tm.currentThread; - currentThread.dispatch(this.notifyCallError.bind(this, -1, errorMsg), - Ci.nsIThread.DISPATCH_NORMAL); - if (DEBUG) { - debug("Number '" + aNumber + "' doesn't seem to be a viable number. Drop."); - } - - return false; - }, - - _updateDebugFlag: function _updateDebugFlag() { - try { - DEBUG = RIL.DEBUG_RIL || - Services.prefs.getBoolPref("ril.debugging.enabled"); - } catch (e) {} - }, - - /** - * nsITelephonyProvider interface. - */ - - registerListener: function(aListener) { - if (this._listeners.indexOf(aListener) >= 0) { - throw Cr.NS_ERROR_UNEXPECTED; - } - - this._listeners.push(aListener); - }, - - unregisterListener: function(aListener) { - let index = this._listeners.indexOf(aListener); - if (index < 0) { - throw Cr.NS_ERROR_UNEXPECTED; - } - - this._listeners.splice(index, 1); - }, - - enumerateCalls: function(aListener) { - if (DEBUG) debug("Requesting enumeration of calls for callback"); - gRadioInterface.sendWorkerMessage("enumerateCalls", null, - (function(response) { - for (let call of response.calls) { - call.state = this._convertRILCallState(call.state); - call.isActive = this._activeCall ? - (call.callIndex == this._activeCall.callIndex) : false; - - aListener.enumerateCallState(call.callIndex, call.state, call.number, - call.isActive, call.isOutgoing, - call.isEmergency, call.isConference); - } - aListener.enumerateCallStateComplete(); - - return false; - }).bind(this)); - }, - - dial: function(aNumber, aIsEmergency) { - if (DEBUG) debug("Dialing " + (aIsEmergency ? "emergency " : "") + aNumber); - // 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 (!aIsEmergency) { - aNumber = gPhoneNumberUtils.normalize(aNumber); - } - if (this._validateNumber(aNumber)) { - gRadioInterface.sendWorkerMessage("dial", { number: aNumber, - isDialEmergency: aIsEmergency }); - } - }, - - hangUp: function(aCallIndex) { - gRadioInterface.sendWorkerMessage("hangUp", { callIndex: aCallIndex }); - }, - - startTone: function(aDtmfChar) { - gRadioInterface.sendWorkerMessage("startTone", { dtmfChar: aDtmfChar }); - }, - - stopTone: function() { - gRadioInterface.sendWorkerMessage("stopTone"); - }, - - answerCall: function(aCallIndex) { - gRadioInterface.sendWorkerMessage("answerCall", { callIndex: aCallIndex }); - }, - - rejectCall: function(aCallIndex) { - gRadioInterface.sendWorkerMessage("rejectCall", { callIndex: aCallIndex }); - }, - - holdCall: function(aCallIndex) { - gRadioInterface.sendWorkerMessage("holdCall", { callIndex: aCallIndex }); - }, - - resumeCall: function(aCallIndex) { - gRadioInterface.sendWorkerMessage("resumeCall", { callIndex: aCallIndex }); - }, - - conferenceCall: function conferenceCall() { - gRadioInterface.sendWorkerMessage("conferenceCall"); - }, - - separateCall: function separateCall(aCallIndex) { - gRadioInterface.sendWorkerMessage("separateCall", { callIndex: aCallIndex }); - }, - - holdConference: function holdConference() { - gRadioInterface.sendWorkerMessage("holdConference"); - }, - - resumeConference: function resumeConference() { - gRadioInterface.sendWorkerMessage("resumeConference"); - }, - - get microphoneMuted() { - return gAudioManager.microphoneMuted; - }, - - set microphoneMuted(aMuted) { - if (aMuted == this.microphoneMuted) { - return; - } - gAudioManager.microphoneMuted = aMuted; - - if (!this._activeCall) { - gAudioManager.phoneState = nsIAudioManager.PHONE_STATE_NORMAL; - } - }, - - get speakerEnabled() { - let force = gAudioManager.getForceForUse(nsIAudioManager.USE_COMMUNICATION); - return (force == nsIAudioManager.FORCE_SPEAKER); - }, - - set speakerEnabled(aEnabled) { - if (aEnabled == this.speakerEnabled) { - return; - } - let force = aEnabled ? nsIAudioManager.FORCE_SPEAKER : - nsIAudioManager.FORCE_NONE; - gAudioManager.setForceForUse(nsIAudioManager.USE_COMMUNICATION, force); - - if (!this._activeCall) { - gAudioManager.phoneState = nsIAudioManager.PHONE_STATE_NORMAL; - } - }, - - /** - * nsIGonkTelephonyProvider interface. - */ - - /** - * Handle call disconnects by updating our current state and the audio system. - */ - notifyCallDisconnected: function notifyCallDisconnected(aCall) { - if (DEBUG) debug("handleCallDisconnected: " + JSON.stringify(aCall)); - - aCall.state = nsITelephonyProvider.CALL_STATE_DISCONNECTED; - let duration = ("started" in aCall && typeof aCall.started == "number") ? - new Date().getTime() - aCall.started : 0; - let data = { - number: aCall.number, - duration: duration, - direction: aCall.isOutgoing ? "outgoing" : "incoming" - }; - gSystemMessenger.broadcastMessage("telephony-call-ended", data); - - this._updateCallAudioState(aCall, null); - - this._notifyAllListeners("callStateChanged", [aCall.callIndex, - aCall.state, - aCall.number, - aCall.isActive, - aCall.isOutgoing, - aCall.isEmergency, - aCall.isConference]); - }, - - /** - * Handle call error. - */ - notifyCallError: function notifyCallError(aCallIndex, aErrorMsg) { - this._notifyAllListeners("notifyError", [aCallIndex, aErrorMsg]); - }, - - /** - * Handle an incoming call. - * - * Not much is known about this call at this point, but it's enough - * to start bringing up the Phone app already. - */ - notifyCallRing: function notifyCallRing() { - if (!this._callRingWakeLock) { - this._callRingWakeLock = gPowerManagerService.newWakeLock("cpu"); - } - if (!this._callRingWakeLockTimer) { - this._callRingWakeLockTimer = - Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); - } - this._callRingWakeLockTimer - .initWithCallback(this._cancelCallRingWakeLockTimer.bind(this), - CALL_WAKELOCK_TIMEOUT, Ci.nsITimer.TYPE_ONE_SHOT); - - gSystemMessenger.broadcastMessage("telephony-new-call", {}); - }, - - /** - * Handle call state changes by updating our current state and the audio - * system. - */ - notifyCallStateChanged: function notifyCallStateChanged(aCall) { - if (DEBUG) debug("handleCallStateChange: " + JSON.stringify(aCall)); - - aCall.state = this._convertRILCallState(aCall.state); - if (aCall.state == nsITelephonyProvider.CALL_STATE_DIALING) { - gSystemMessenger.broadcastMessage("telephony-new-call", {}); - } - - this._updateCallAudioState(aCall, null); - - this._notifyAllListeners("callStateChanged", [aCall.callIndex, - aCall.state, - aCall.number, - aCall.isActive, - aCall.isOutgoing, - aCall.isEmergency, - aCall.isConference]); - }, - - notifyCdmaCallWaiting: function notifyCdmaCallWaiting(aNumber) { - this._notifyAllListeners("notifyCdmaCallWaiting", [aNumber]); - }, - - notifySupplementaryService: function notifySupplementaryService(aCallIndex, - aNotification) { - let notification = this._convertRILSuppSvcNotification(aNotification); - this._notifyAllListeners("supplementaryServiceNotification", - [aCallIndex, notification]); - }, - - notifyConferenceCallStateChanged: function notifyConferenceCallStateChanged(aState) { - if (DEBUG) debug("handleConferenceCallStateChanged: " + aState); - aState = aState != null ? convertRILCallState(aState) : - nsITelephonyProvider.CALL_STATE_UNKNOWN; - this._updateCallAudioState(null, aState); - - this._notifyAllListeners("conferenceCallStateChanged", [aState]); - }, - - /** - * nsIObserver interface. - */ - - observe: function observe(aSubject, aTopic, aData) { - switch (aTopic) { - case kPrefenceChangedObserverTopic: - if (aData === "ril.debugging.enabled") { - this._updateDebugFlag(); - } - break; - - case kXpcomShutdownObserverTopic: - // Cancel the timer for the call-ring wake lock. - this._cancelCallRingWakeLockTimer(); - - Services.obs.removeObserver(this, kPrefenceChangedObserverTopic); - Services.obs.removeObserver(this, kXpcomShutdownObserverTopic); - break; - } - } -}; - -this.NSGetFactory = XPCOMUtils.generateNSGetFactory([TelephonyProvider]); diff --git a/dom/telephony/gonk/TelephonyProvider.manifest b/dom/telephony/gonk/TelephonyProvider.manifest deleted file mode 100644 index 06cfba0c7983..000000000000 --- a/dom/telephony/gonk/TelephonyProvider.manifest +++ /dev/null @@ -1,2 +0,0 @@ -component {67d26434-d063-4d28-9f48-5b3189788155} TelephonyProvider.js -contract @mozilla.org/telephony/gonktelephonyprovider;1 {67d26434-d063-4d28-9f48-5b3189788155} diff --git a/dom/telephony/ipc/PTelephony.ipdl b/dom/telephony/ipc/PTelephony.ipdl deleted file mode 100644 index d82044401ef5..000000000000 --- a/dom/telephony/ipc/PTelephony.ipdl +++ /dev/null @@ -1,83 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set sw=2 ts=8 et ft=cpp : */ -/* 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 protocol PContent; -include protocol PTelephonyRequest; -include TelephonyTypes; - -namespace mozilla { -namespace dom { -namespace telephony { - -sync protocol PTelephony { - manager PContent; - manages PTelephonyRequest; - -child: - NotifyCallError(int32_t aCallIndex, nsString aError); - - NotifyCallStateChanged(IPCCallStateData aData); - - NotifyCdmaCallWaiting(nsString aNumber); - - NotifyConferenceCallStateChanged(uint16_t aCallState); - - NotifySupplementaryService(int32_t aCallIndex, uint16_t aNotification); - -parent: - /** - * Sent when the child no longer needs to use PTelephony. - */ - __delete__(); - - /** - * Sent when the child makes an asynchronous request to the parent. It's - * currently only for request call enumeration. - */ - PTelephonyRequest(); - - RegisterListener(); - - UnregisterListener(); - - DialCall(nsString aNumber, bool aIsEmergency); - - HangUpCall(uint32_t aCallIndex); - - AnswerCall(uint32_t aCallIndex); - - RejectCall(uint32_t aCallIndex); - - HoldCall(uint32_t aCallIndex); - - ResumeCall(uint32_t aCallIndex); - - ConferenceCall(); - - SeparateCall(uint32_t aCallIndex); - - HoldConference(); - - ResumeConference(); - - StartTone(nsString aTone); - - StopTone(); - - sync GetMicrophoneMuted() - returns (bool aMuted); - - SetMicrophoneMuted(bool aMuted); - - sync GetSpeakerEnabled() - returns (bool aEnabled); - - SetSpeakerEnabled(bool aEnabled); -}; - -} /* namespace telephony */ -} /* namespace dom */ -} /* namespace mozilla */ diff --git a/dom/telephony/ipc/PTelephonyRequest.ipdl b/dom/telephony/ipc/PTelephonyRequest.ipdl deleted file mode 100644 index 05f708ffc6a0..000000000000 --- a/dom/telephony/ipc/PTelephonyRequest.ipdl +++ /dev/null @@ -1,30 +0,0 @@ -/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */ -/* vim: set ts=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/. */ - -include protocol PTelephony; -include TelephonyTypes; - -namespace mozilla { -namespace dom { -namespace telephony { - -protocol PTelephonyRequest -{ - manager PTelephony; - -child: - NotifyEnumerateCallState(IPCCallStateData aData); - - /** - * Sent when the asynchronous request has completed. It's currently only for - * request call enumeration. - */ - __delete__(); -}; - -} /* namespace telephony */ -} /* namespace dom */ -} /* namespace mozilla */ diff --git a/dom/telephony/ipc/TelephonyChild.cpp b/dom/telephony/ipc/TelephonyChild.cpp deleted file mode 100644 index 411e3b35f0a7..000000000000 --- a/dom/telephony/ipc/TelephonyChild.cpp +++ /dev/null @@ -1,130 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 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/. */ - -#include "mozilla/dom/telephony/TelephonyChild.h" - -USING_TELEPHONY_NAMESPACE - -/******************************************************************************* - * TelephonyChild - ******************************************************************************/ - -TelephonyChild::TelephonyChild(nsITelephonyListener* aListener) - : mListener(aListener) -{ - MOZ_ASSERT(aListener); -} - -void -TelephonyChild::ActorDestroy(ActorDestroyReason aWhy) -{ - mListener = nullptr; -} - -PTelephonyRequestChild* -TelephonyChild::AllocPTelephonyRequestChild() -{ - MOZ_CRASH("Caller is supposed to manually construct a request!"); -} - -bool -TelephonyChild::DeallocPTelephonyRequestChild(PTelephonyRequestChild* aActor) -{ - delete aActor; - return true; -} - -bool -TelephonyChild::RecvNotifyCallError(const int32_t& aCallIndex, - const nsString& aError) -{ - MOZ_ASSERT(mListener); - - mListener->NotifyError(aCallIndex, aError); - return true; -} - -bool -TelephonyChild::RecvNotifyCallStateChanged(const IPCCallStateData& aData) -{ - MOZ_ASSERT(mListener); - - mListener->CallStateChanged(aData.callIndex(), - aData.callState(), - aData.number(), - aData.isActive(), - aData.isOutGoing(), - aData.isEmergency(), - aData.isConference()); - return true; -} - -bool -TelephonyChild::RecvNotifyCdmaCallWaiting(const nsString& aNumber) -{ - MOZ_ASSERT(mListener); - - mListener->NotifyCdmaCallWaiting(aNumber); - return true; -} - -bool -TelephonyChild::RecvNotifyConferenceCallStateChanged(const uint16_t& aCallState) -{ - MOZ_ASSERT(mListener); - - mListener->ConferenceCallStateChanged(aCallState); - return true; -} - -bool -TelephonyChild::RecvNotifySupplementaryService(const int32_t& aCallIndex, - const uint16_t& aNotification) -{ - MOZ_ASSERT(mListener); - - mListener->SupplementaryServiceNotification(aCallIndex, aNotification); - return true; -} - -/******************************************************************************* - * TelephonyRequestChild - ******************************************************************************/ - -TelephonyRequestChild::TelephonyRequestChild(nsITelephonyListener* aListener) - : mListener(aListener) -{ - MOZ_ASSERT(aListener); -} - -void -TelephonyRequestChild::ActorDestroy(ActorDestroyReason aWhy) -{ - mListener = nullptr; -} - -bool -TelephonyRequestChild::Recv__delete__() -{ - MOZ_ASSERT(mListener); - - mListener->EnumerateCallStateComplete(); - return true; -} - -bool -TelephonyRequestChild::RecvNotifyEnumerateCallState(const IPCCallStateData& aData) -{ - MOZ_ASSERT(mListener); - - mListener->EnumerateCallState(aData.callIndex(), - aData.callState(), - aData.number(), - aData.isActive(), - aData.isOutGoing(), - aData.isEmergency(), - aData.isConference()); - return true; -} diff --git a/dom/telephony/ipc/TelephonyChild.h b/dom/telephony/ipc/TelephonyChild.h deleted file mode 100644 index 91eb1f8d2a54..000000000000 --- a/dom/telephony/ipc/TelephonyChild.h +++ /dev/null @@ -1,77 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 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/. */ - -#ifndef mozilla_dom_telephony_TelephonyChild_h -#define mozilla_dom_telephony_TelephonyChild_h - -#include "mozilla/dom/telephony/TelephonyCommon.h" -#include "mozilla/dom/telephony/PTelephonyChild.h" -#include "mozilla/dom/telephony/PTelephonyRequestChild.h" -#include "nsITelephonyProvider.h" - -BEGIN_TELEPHONY_NAMESPACE - -class TelephonyChild : public PTelephonyChild -{ -public: - TelephonyChild(nsITelephonyListener* aListener); - -protected: - virtual ~TelephonyChild() {} - - virtual void - ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE; - - virtual PTelephonyRequestChild* - AllocPTelephonyRequestChild() MOZ_OVERRIDE; - - virtual bool - DeallocPTelephonyRequestChild(PTelephonyRequestChild* aActor) MOZ_OVERRIDE; - - virtual bool - RecvNotifyCallError(const int32_t& aCallIndex, - const nsString& aError) MOZ_OVERRIDE; - - virtual bool - RecvNotifyCallStateChanged(const IPCCallStateData& aData) MOZ_OVERRIDE; - - virtual bool - RecvNotifyCdmaCallWaiting(const nsString& aNumber) MOZ_OVERRIDE; - - virtual bool - RecvNotifyConferenceCallStateChanged(const uint16_t& aCallState) MOZ_OVERRIDE; - - virtual bool - RecvNotifySupplementaryService(const int32_t& aCallIndex, - const uint16_t& aNotification) MOZ_OVERRIDE; - -private: - nsCOMPtr mListener; -}; - -class TelephonyRequestChild : public PTelephonyRequestChild -{ -public: - TelephonyRequestChild(nsITelephonyListener* aListener); - -protected: - virtual ~TelephonyRequestChild() {} - - virtual void - ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE; - - virtual bool - Recv__delete__() MOZ_OVERRIDE; - - virtual bool - RecvNotifyEnumerateCallState(const IPCCallStateData& aData) MOZ_OVERRIDE; - -private: - nsCOMPtr mListener; -}; - -END_TELEPHONY_NAMESPACE - -#endif // mozilla_dom_telephony_TelephonyChild_h diff --git a/dom/telephony/ipc/TelephonyIPCProvider.cpp b/dom/telephony/ipc/TelephonyIPCProvider.cpp deleted file mode 100644 index 3b40a31271c3..000000000000 --- a/dom/telephony/ipc/TelephonyIPCProvider.cpp +++ /dev/null @@ -1,258 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 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/. */ - -#include "mozilla/dom/ContentChild.h" -#include "mozilla/dom/telephony/TelephonyChild.h" -#include "ipc/TelephonyIPCProvider.h" - -USING_TELEPHONY_NAMESPACE -using namespace mozilla::dom; - -NS_IMPL_ISUPPORTS2(TelephonyIPCProvider, - nsITelephonyProvider, - nsITelephonyListener) - -TelephonyIPCProvider::TelephonyIPCProvider() -{ - // Deallocated in ContentChild::DeallocPTelephonyChild(). - mPTelephonyChild = new TelephonyChild(this); - ContentChild::GetSingleton()->SendPTelephonyConstructor(mPTelephonyChild); -} - -TelephonyIPCProvider::~TelephonyIPCProvider() -{ - mPTelephonyChild->Send__delete__(mPTelephonyChild); - mPTelephonyChild = nullptr; -} - -/* - * Implementation of nsITelephonyProvider. - */ - -NS_IMETHODIMP -TelephonyIPCProvider::RegisterListener(nsITelephonyListener *aListener) -{ - MOZ_ASSERT(!mListeners.Contains(aListener)); - - // nsTArray doesn't fail. - mListeners.AppendElement(aListener); - - if (mListeners.Length() == 1) { - mPTelephonyChild->SendRegisterListener(); - } - return NS_OK; -} - -NS_IMETHODIMP -TelephonyIPCProvider::UnregisterListener(nsITelephonyListener *aListener) -{ - MOZ_ASSERT(mListeners.Contains(aListener)); - - // We always have the element here, so it can't fail. - mListeners.RemoveElement(aListener); - - if (!mListeners.Length()) { - mPTelephonyChild->SendUnregisterListener(); - } - return NS_OK; -} - -NS_IMETHODIMP -TelephonyIPCProvider::EnumerateCalls(nsITelephonyListener *aListener) -{ - // Life time of newly allocated TelephonyRequestChild instance is managed by - // IPDL itself. - TelephonyRequestChild* actor = new TelephonyRequestChild(aListener); - mPTelephonyChild->SendPTelephonyRequestConstructor(actor); - return NS_OK; -} - -NS_IMETHODIMP -TelephonyIPCProvider::Dial(const nsAString& aNumber, - bool aIsEmergency) -{ - mPTelephonyChild->SendDialCall(nsString(aNumber), aIsEmergency); - return NS_OK; -} - -NS_IMETHODIMP -TelephonyIPCProvider::HangUp(uint32_t aCallIndex) -{ - mPTelephonyChild->SendHangUpCall(aCallIndex); - return NS_OK; -} - -NS_IMETHODIMP -TelephonyIPCProvider::AnswerCall(uint32_t aCallIndex) -{ - mPTelephonyChild->SendAnswerCall(aCallIndex); - return NS_OK; -} - -NS_IMETHODIMP -TelephonyIPCProvider::RejectCall(uint32_t aCallIndex) -{ - mPTelephonyChild->SendRejectCall(aCallIndex); - return NS_OK; -} - -NS_IMETHODIMP -TelephonyIPCProvider::HoldCall(uint32_t aCallIndex) -{ - mPTelephonyChild->SendHoldCall(aCallIndex); - return NS_OK; -} - -NS_IMETHODIMP -TelephonyIPCProvider::ResumeCall(uint32_t aCallIndex) -{ - mPTelephonyChild->SendResumeCall(aCallIndex); - return NS_OK; -} - -NS_IMETHODIMP -TelephonyIPCProvider::ConferenceCall() -{ - mPTelephonyChild->SendConferenceCall(); - return NS_OK; -} - -NS_IMETHODIMP -TelephonyIPCProvider::SeparateCall(uint32_t aCallIndex) -{ - mPTelephonyChild->SendSeparateCall(aCallIndex); - return NS_OK; -} - -NS_IMETHODIMP -TelephonyIPCProvider::HoldConference() -{ - mPTelephonyChild->SendHoldConference(); - return NS_OK; -} - -NS_IMETHODIMP -TelephonyIPCProvider::ResumeConference() -{ - mPTelephonyChild->SendResumeConference(); - return NS_OK; -} - -NS_IMETHODIMP -TelephonyIPCProvider::StartTone(const nsAString& aDtmfChar) -{ - mPTelephonyChild->SendStartTone(nsString(aDtmfChar)); - return NS_OK; -} - -NS_IMETHODIMP -TelephonyIPCProvider::StopTone() -{ - mPTelephonyChild->SendStopTone(); - return NS_OK; -} - -NS_IMETHODIMP -TelephonyIPCProvider::GetMicrophoneMuted(bool* aMuted) -{ - mPTelephonyChild->SendGetMicrophoneMuted(aMuted); - return NS_OK; -} - -NS_IMETHODIMP -TelephonyIPCProvider::SetMicrophoneMuted(bool aMuted) -{ - mPTelephonyChild->SendSetMicrophoneMuted(aMuted); - return NS_OK; -} - -NS_IMETHODIMP -TelephonyIPCProvider::GetSpeakerEnabled(bool* aEnabled) -{ - mPTelephonyChild->SendGetSpeakerEnabled(aEnabled); - return NS_OK; -} - -NS_IMETHODIMP -TelephonyIPCProvider::SetSpeakerEnabled(bool aEnabled) -{ - mPTelephonyChild->SendSetSpeakerEnabled(aEnabled); - return NS_OK; -} - -// nsITelephonyListener - -NS_IMETHODIMP -TelephonyIPCProvider::CallStateChanged(uint32_t aCallIndex, - uint16_t aCallState, - const nsAString& aNumber, - bool aIsActive, - bool aIsOutgoing, - bool aIsEmergency, - bool aIsConference) -{ - for (uint32_t i = 0; i < mListeners.Length(); i++) { - mListeners[i]->CallStateChanged(aCallIndex, aCallState, aNumber, - aIsActive, aIsOutgoing, aIsEmergency, - aIsConference); - } - return NS_OK; -} - -NS_IMETHODIMP -TelephonyIPCProvider::ConferenceCallStateChanged(uint16_t aCallState) -{ - for (uint32_t i = 0; i < mListeners.Length(); i++) { - mListeners[i]->ConferenceCallStateChanged(aCallState); - } - return NS_OK; -} - -NS_IMETHODIMP -TelephonyIPCProvider::EnumerateCallStateComplete() -{ - MOZ_CRASH("Not a EnumerateCalls request!"); -} - -NS_IMETHODIMP -TelephonyIPCProvider::EnumerateCallState(uint32_t aCallIndex, - uint16_t aCallState, - const nsAString& aNumber, - bool aIsActive, - bool aIsOutgoing, - bool aIsEmergency, - bool aIsConference) -{ - MOZ_CRASH("Not a EnumerateCalls request!"); -} - -NS_IMETHODIMP -TelephonyIPCProvider::NotifyCdmaCallWaiting(const nsAString& aNumber) -{ - for (uint32_t i = 0; i < mListeners.Length(); i++) { - mListeners[i]->NotifyCdmaCallWaiting(aNumber); - } - return NS_OK; -} - -NS_IMETHODIMP -TelephonyIPCProvider::NotifyError(int32_t aCallIndex, - const nsAString& aError) -{ - for (uint32_t i = 0; i < mListeners.Length(); i++) { - mListeners[i]->NotifyError(aCallIndex, aError); - } - return NS_OK; -} - -NS_IMETHODIMP -TelephonyIPCProvider::SupplementaryServiceNotification(int32_t aCallIndex, - uint16_t aNotification) -{ - for (uint32_t i = 0; i < mListeners.Length(); i++) { - mListeners[i]->SupplementaryServiceNotification(aCallIndex, aNotification); - } - return NS_OK; -} diff --git a/dom/telephony/ipc/TelephonyIPCProvider.h b/dom/telephony/ipc/TelephonyIPCProvider.h deleted file mode 100644 index 928fd1d6567c..000000000000 --- a/dom/telephony/ipc/TelephonyIPCProvider.h +++ /dev/null @@ -1,37 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 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/. */ - -#ifndef mozilla_dom_telephony_TelephonyIPCProvider_h -#define mozilla_dom_telephony_TelephonyIPCProvider_h - -#include "mozilla/dom/telephony/TelephonyCommon.h" -#include "mozilla/Attributes.h" -#include "nsITelephonyProvider.h" - -BEGIN_TELEPHONY_NAMESPACE - -class PTelephonyChild; - -class TelephonyIPCProvider MOZ_FINAL : public nsITelephonyProvider - , public nsITelephonyListener -{ -public: - NS_DECL_ISUPPORTS - NS_DECL_NSITELEPHONYPROVIDER - NS_DECL_NSITELEPHONYLISTENER - - TelephonyIPCProvider(); - -protected: - virtual ~TelephonyIPCProvider(); - -private: - nsTArray > mListeners; - PTelephonyChild* mPTelephonyChild; -}; - -END_TELEPHONY_NAMESPACE - -#endif // mozilla_dom_telephony_TelephonyIPCProvider_h diff --git a/dom/telephony/ipc/TelephonyParent.cpp b/dom/telephony/ipc/TelephonyParent.cpp deleted file mode 100644 index c55a90b8553d..000000000000 --- a/dom/telephony/ipc/TelephonyParent.cpp +++ /dev/null @@ -1,448 +0,0 @@ -/* -*- Mode: C++ tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 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/. */ - -#include "mozilla/dom/telephony/TelephonyParent.h" - -USING_TELEPHONY_NAMESPACE - -/******************************************************************************* - * TelephonyParent - ******************************************************************************/ - -NS_IMPL_ISUPPORTS1(TelephonyParent, nsITelephonyListener) - -TelephonyParent::TelephonyParent() - : mActorDestroyed(false) - , mRegistered(false) -{ -} - -void -TelephonyParent::ActorDestroy(ActorDestroyReason why) -{ - // The child process could die before this asynchronous notification, in which - // case ActorDestroy() was called and mActorDestroyed is set to true. Return - // an error here to avoid sending a message to the dead process. - mActorDestroyed = true; - - // Try to unregister listener if we're still registered. - RecvUnregisterListener(); -} - -bool -TelephonyParent::RecvPTelephonyRequestConstructor(PTelephonyRequestParent* aActor) -{ - TelephonyRequestParent* actor = static_cast(aActor); - - return actor->DoRequest(); -} - -PTelephonyRequestParent* -TelephonyParent::AllocPTelephonyRequestParent() -{ - TelephonyRequestParent* actor = new TelephonyRequestParent(); - // Add an extra ref for IPDL. Will be released in - // TelephonyParent::DeallocPTelephonyRequestParent(). - NS_ADDREF(actor); - - return actor; -} - -bool -TelephonyParent::DeallocPTelephonyRequestParent(PTelephonyRequestParent* aActor) -{ - // TelephonyRequestParent is refcounted, must not be freed manually. - static_cast(aActor)->Release(); - return true; -} - -bool -TelephonyParent::Recv__delete__() -{ - return true; // Unregister listener in TelephonyParent::ActorDestroy(). -} - -bool -TelephonyParent::RecvRegisterListener() -{ - NS_ENSURE_TRUE(!mRegistered, true); - - nsCOMPtr provider = - do_GetService(TELEPHONY_PROVIDER_CONTRACTID); - NS_ENSURE_TRUE(provider, true); - - mRegistered = NS_SUCCEEDED(provider->RegisterListener(this)); - return true; -} - -bool -TelephonyParent::RecvUnregisterListener() -{ - NS_ENSURE_TRUE(mRegistered, true); - - nsCOMPtr provider = - do_GetService(TELEPHONY_PROVIDER_CONTRACTID); - NS_ENSURE_TRUE(provider, true); - - mRegistered = !NS_SUCCEEDED(provider->UnregisterListener(this)); - return true; -} - -bool -TelephonyParent::RecvDialCall(const nsString& aNumber, - const bool& aIsEmergency) -{ - nsCOMPtr provider = - do_GetService(TELEPHONY_PROVIDER_CONTRACTID); - NS_ENSURE_TRUE(provider, true); - - provider->Dial(aNumber, aIsEmergency); - return true; -} - -bool -TelephonyParent::RecvHangUpCall(const uint32_t& aCallIndex) -{ - nsCOMPtr provider = - do_GetService(TELEPHONY_PROVIDER_CONTRACTID); - NS_ENSURE_TRUE(provider, true); - - provider->HangUp(aCallIndex); - return true; -} - -bool -TelephonyParent::RecvAnswerCall(const uint32_t& aCallIndex) -{ - nsCOMPtr provider = - do_GetService(TELEPHONY_PROVIDER_CONTRACTID); - NS_ENSURE_TRUE(provider, true); - - provider->AnswerCall(aCallIndex); - return true; -} - -bool -TelephonyParent::RecvRejectCall(const uint32_t& aCallIndex) -{ - nsCOMPtr provider = - do_GetService(TELEPHONY_PROVIDER_CONTRACTID); - NS_ENSURE_TRUE(provider, true); - - provider->RejectCall(aCallIndex); - return true; -} - -bool -TelephonyParent::RecvHoldCall(const uint32_t& aCallIndex) -{ - nsCOMPtr provider = - do_GetService(TELEPHONY_PROVIDER_CONTRACTID); - NS_ENSURE_TRUE(provider, true); - - provider->HoldCall(aCallIndex); - return true; -} - -bool -TelephonyParent::RecvResumeCall(const uint32_t& aCallIndex) -{ - nsCOMPtr provider = - do_GetService(TELEPHONY_PROVIDER_CONTRACTID); - NS_ENSURE_TRUE(provider, true); - - provider->ResumeCall(aCallIndex); - return true; -} - -bool -TelephonyParent::RecvConferenceCall() -{ - nsCOMPtr provider = - do_GetService(TELEPHONY_PROVIDER_CONTRACTID); - NS_ENSURE_TRUE(provider, true); - - provider->ConferenceCall(); - return true; -} - -bool -TelephonyParent::RecvSeparateCall(const uint32_t& aCallState) -{ - nsCOMPtr provider = - do_GetService(TELEPHONY_PROVIDER_CONTRACTID); - NS_ENSURE_TRUE(provider, true); - - provider->SeparateCall(aCallState); - return true; -} - -bool -TelephonyParent::RecvHoldConference() -{ - nsCOMPtr provider = - do_GetService(TELEPHONY_PROVIDER_CONTRACTID); - NS_ENSURE_TRUE(provider, true); - - provider->HoldConference(); - return true; -} - -bool -TelephonyParent::RecvResumeConference() -{ - nsCOMPtr provider = - do_GetService(TELEPHONY_PROVIDER_CONTRACTID); - NS_ENSURE_TRUE(provider, true); - - provider->ResumeConference(); - return true; -} - -bool -TelephonyParent::RecvStartTone(const nsString& aTone) -{ - nsCOMPtr provider = - do_GetService(TELEPHONY_PROVIDER_CONTRACTID); - NS_ENSURE_TRUE(provider, true); - - provider->StartTone(aTone); - return true; -} - -bool -TelephonyParent::RecvStopTone() -{ - nsCOMPtr provider = - do_GetService(TELEPHONY_PROVIDER_CONTRACTID); - NS_ENSURE_TRUE(provider, true); - - provider->StopTone(); - return true; -} - -bool -TelephonyParent::RecvGetMicrophoneMuted(bool* aMuted) -{ - *aMuted = false; - - nsCOMPtr provider = - do_GetService(TELEPHONY_PROVIDER_CONTRACTID); - NS_ENSURE_TRUE(provider, true); - - provider->GetMicrophoneMuted(aMuted); - return true; -} - -bool -TelephonyParent::RecvSetMicrophoneMuted(const bool& aMuted) -{ - nsCOMPtr provider = - do_GetService(TELEPHONY_PROVIDER_CONTRACTID); - NS_ENSURE_TRUE(provider, true); - - provider->SetMicrophoneMuted(aMuted); - return true; -} - -bool -TelephonyParent::RecvGetSpeakerEnabled(bool* aEnabled) -{ - *aEnabled = false; - - nsCOMPtr provider = - do_GetService(TELEPHONY_PROVIDER_CONTRACTID); - NS_ENSURE_TRUE(provider, true); - - provider->GetSpeakerEnabled(aEnabled); - return true; -} - -bool -TelephonyParent::RecvSetSpeakerEnabled(const bool& aEnabled) -{ - nsCOMPtr provider = - do_GetService(TELEPHONY_PROVIDER_CONTRACTID); - NS_ENSURE_TRUE(provider, true); - - provider->SetSpeakerEnabled(aEnabled); - return true; -} - -// nsITelephonyListener - -NS_IMETHODIMP -TelephonyParent::CallStateChanged(uint32_t aCallIndex, - uint16_t aCallState, - const nsAString& aNumber, - bool aIsActive, - bool aIsOutgoing, - bool aIsEmergency, - bool aIsConference) -{ - NS_ENSURE_TRUE(!mActorDestroyed, NS_ERROR_FAILURE); - - IPCCallStateData data(aCallIndex, aCallState, nsString(aNumber), aIsActive, - aIsOutgoing, aIsEmergency, aIsConference); - return SendNotifyCallStateChanged(data) ? 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() -{ - MOZ_CRASH("Not a EnumerateCalls request!"); -} - -NS_IMETHODIMP -TelephonyParent::EnumerateCallState(uint32_t aCallIndex, - uint16_t aCallState, - const nsAString& aNumber, - bool aIsActive, - bool aIsOutgoing, - bool aIsEmergency, - bool aIsConference) -{ - MOZ_CRASH("Not a EnumerateCalls request!"); -} - -NS_IMETHODIMP -TelephonyParent::NotifyCdmaCallWaiting(const nsAString& aNumber) -{ - NS_ENSURE_TRUE(!mActorDestroyed, NS_ERROR_FAILURE); - - return SendNotifyCdmaCallWaiting(nsString(aNumber)) ? NS_OK - : NS_ERROR_FAILURE; -} - -NS_IMETHODIMP -TelephonyParent::NotifyError(int32_t aCallIndex, - const nsAString& aError) -{ - NS_ENSURE_TRUE(!mActorDestroyed, NS_ERROR_FAILURE); - - return SendNotifyCallError(aCallIndex, nsString(aError)) ? NS_OK - : NS_ERROR_FAILURE; -} - -NS_IMETHODIMP -TelephonyParent::SupplementaryServiceNotification(int32_t aCallIndex, - uint16_t aNotification) -{ - NS_ENSURE_TRUE(!mActorDestroyed, NS_ERROR_FAILURE); - - return SendNotifySupplementaryService(aCallIndex, aNotification) - ? NS_OK : NS_ERROR_FAILURE; -} - -/******************************************************************************* - * TelephonyRequestParent - ******************************************************************************/ - -NS_IMPL_ISUPPORTS1(TelephonyRequestParent, nsITelephonyListener) - -TelephonyRequestParent::TelephonyRequestParent() - : mActorDestroyed(false) -{ -} - -void -TelephonyRequestParent::ActorDestroy(ActorDestroyReason why) -{ - // The child process could die before this asynchronous notification, in which - // case ActorDestroy() was called and mActorDestroyed is set to true. Return - // an error here to avoid sending a message to the dead process. - mActorDestroyed = true; -} - -bool -TelephonyRequestParent::DoRequest() -{ - nsresult rv = NS_ERROR_FAILURE; - - nsCOMPtr provider = - do_GetService(TELEPHONY_PROVIDER_CONTRACTID); - if (provider) { - rv = provider->EnumerateCalls(this); - } - - if (NS_FAILED(rv)) { - return NS_SUCCEEDED(EnumerateCallStateComplete()); - } - - return true; -} - -// nsITelephonyListener - -NS_IMETHODIMP -TelephonyRequestParent::CallStateChanged(uint32_t aCallIndex, - uint16_t aCallState, - const nsAString& aNumber, - bool aIsActive, - bool aIsOutgoing, - bool aIsEmergency, - bool aIsConference) -{ - MOZ_CRASH("Not a TelephonyParent!"); -} - -NS_IMETHODIMP -TelephonyRequestParent::ConferenceCallStateChanged(uint16_t aCallState) -{ - MOZ_CRASH("Not a TelephonyParent!"); -} - -NS_IMETHODIMP -TelephonyRequestParent::EnumerateCallStateComplete() -{ - NS_ENSURE_TRUE(!mActorDestroyed, NS_ERROR_FAILURE); - - return Send__delete__(this) ? NS_OK : NS_ERROR_FAILURE; -} - -NS_IMETHODIMP -TelephonyRequestParent::EnumerateCallState(uint32_t aCallIndex, - uint16_t aCallState, - const nsAString& aNumber, - bool aIsActive, - bool aIsOutgoing, - bool aIsEmergency, - bool aIsConference) -{ - NS_ENSURE_TRUE(!mActorDestroyed, NS_ERROR_FAILURE); - - IPCCallStateData data(aCallIndex, aCallState, nsString(aNumber), aIsActive, - aIsOutgoing, aIsEmergency, aIsConference); - return SendNotifyEnumerateCallState(data) ? NS_OK : NS_ERROR_FAILURE; -} - -NS_IMETHODIMP -TelephonyRequestParent::NotifyCdmaCallWaiting(const nsAString& aNumber) -{ - MOZ_CRASH("Not a TelephonyParent!"); -} - -NS_IMETHODIMP -TelephonyRequestParent::NotifyError(int32_t aCallIndex, - const nsAString& aError) -{ - MOZ_CRASH("Not a TelephonyParent!"); -} - -NS_IMETHODIMP -TelephonyRequestParent::SupplementaryServiceNotification(int32_t aCallIndex, - uint16_t aNotification) -{ - MOZ_CRASH("Not a TelephonyParent!"); -} diff --git a/dom/telephony/ipc/TelephonyParent.h b/dom/telephony/ipc/TelephonyParent.h deleted file mode 100644 index ac508c264bea..000000000000 --- a/dom/telephony/ipc/TelephonyParent.h +++ /dev/null @@ -1,128 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 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/. */ - -#ifndef mozilla_dom_telephony_TelephonyParent_h -#define mozilla_dom_telephony_TelephonyParent_h - -#include "mozilla/dom/telephony/TelephonyCommon.h" -#include "mozilla/dom/telephony/PTelephonyParent.h" -#include "mozilla/dom/telephony/PTelephonyRequestParent.h" -#include "nsITelephonyProvider.h" - -BEGIN_TELEPHONY_NAMESPACE - -class TelephonyParent : public PTelephonyParent - , public nsITelephonyListener -{ -public: - NS_DECL_ISUPPORTS - NS_DECL_NSITELEPHONYLISTENER - - TelephonyParent(); - -protected: - virtual ~TelephonyParent() {} - - virtual void - ActorDestroy(ActorDestroyReason why); - - virtual bool - RecvPTelephonyRequestConstructor(PTelephonyRequestParent* aActor) MOZ_OVERRIDE; - - virtual PTelephonyRequestParent* - AllocPTelephonyRequestParent() MOZ_OVERRIDE; - - virtual bool - DeallocPTelephonyRequestParent(PTelephonyRequestParent* aActor) MOZ_OVERRIDE; - - virtual bool - Recv__delete__() MOZ_OVERRIDE; - - virtual bool - RecvRegisterListener() MOZ_OVERRIDE; - - virtual bool - RecvUnregisterListener() MOZ_OVERRIDE; - - virtual bool - RecvDialCall(const nsString& aNumber, - const bool& aIsEmergency) MOZ_OVERRIDE; - - virtual bool - RecvHangUpCall(const uint32_t& aCallIndex) MOZ_OVERRIDE; - - virtual bool - RecvAnswerCall(const uint32_t& aCallIndex) MOZ_OVERRIDE; - - virtual bool - RecvRejectCall(const uint32_t& aCallIndex) MOZ_OVERRIDE; - - virtual bool - RecvHoldCall(const uint32_t& aCallIndex) MOZ_OVERRIDE; - - virtual bool - RecvResumeCall(const uint32_t& aCallIndex) MOZ_OVERRIDE; - - virtual bool - RecvConferenceCall() MOZ_OVERRIDE; - - virtual bool - RecvSeparateCall(const uint32_t& callIndex) MOZ_OVERRIDE; - - virtual bool - RecvHoldConference() MOZ_OVERRIDE; - - virtual bool - RecvResumeConference() MOZ_OVERRIDE; - - virtual bool - RecvStartTone(const nsString& aTone) MOZ_OVERRIDE; - - virtual bool - RecvStopTone() MOZ_OVERRIDE; - - virtual bool - RecvGetMicrophoneMuted(bool* aMuted) MOZ_OVERRIDE; - - virtual bool - RecvSetMicrophoneMuted(const bool& aMuted) MOZ_OVERRIDE; - - virtual bool - RecvGetSpeakerEnabled(bool* aEnabled) MOZ_OVERRIDE; - - virtual bool - RecvSetSpeakerEnabled(const bool& aEnabled) MOZ_OVERRIDE; - -private: - bool mActorDestroyed; - bool mRegistered; -}; - -class TelephonyRequestParent : public PTelephonyRequestParent - , public nsITelephonyListener -{ - friend class TelephonyParent; - -public: - NS_DECL_ISUPPORTS - NS_DECL_NSITELEPHONYLISTENER - -protected: - TelephonyRequestParent(); - virtual ~TelephonyRequestParent() {} - - virtual void - ActorDestroy(ActorDestroyReason why); - -private: - bool mActorDestroyed; - - bool - DoRequest(); -}; - -END_TELEPHONY_NAMESPACE - -#endif /* mozilla_dom_telephony_TelephonyParent_h */ diff --git a/dom/telephony/ipc/TelephonyTypes.ipdlh b/dom/telephony/ipc/TelephonyTypes.ipdlh deleted file mode 100644 index 5a46e4ace56c..000000000000 --- a/dom/telephony/ipc/TelephonyTypes.ipdlh +++ /dev/null @@ -1,24 +0,0 @@ -/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */ -/* vim: set ts=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/. */ - -namespace mozilla { -namespace dom { -namespace telephony { - -struct IPCCallStateData -{ - uint32_t callIndex; - uint16_t callState; - nsString number; - bool isActive; - bool isOutGoing; - bool isEmergency; - bool isConference; -}; - -} /* namespace telephony */ -} /* namespace dom */ -} /* namespace mozilla */ diff --git a/dom/telephony/moz.build b/dom/telephony/moz.build index 7735929b5fb2..8910a24c887f 100644 --- a/dom/telephony/moz.build +++ b/dom/telephony/moz.build @@ -12,47 +12,17 @@ XPIDL_MODULE = 'dom_telephony' MODULE = 'dom' -EXPORTS.mozilla.dom.telephony += [ - 'CallEvent.h', - 'CallsList.h', - 'Telephony.h', - 'TelephonyCall.h', - 'TelephonyCallGroup.h', - 'TelephonyCommon.h', - 'TelephonyFactory.h', - 'ipc/TelephonyChild.h', - 'ipc/TelephonyParent.h', -] - CPP_SOURCES += [ 'CallEvent.cpp', 'CallsList.cpp', 'Telephony.cpp', 'TelephonyCall.cpp', 'TelephonyCallGroup.cpp', - 'TelephonyFactory.cpp', - 'ipc/TelephonyChild.cpp', - 'ipc/TelephonyIPCProvider.cpp', - 'ipc/TelephonyParent.cpp', ] -IPDL_SOURCES += [ - 'ipc/PTelephony.ipdl', - 'ipc/PTelephonyRequest.ipdl', - 'ipc/TelephonyTypes.ipdlh' -] - -if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk': - XPIDL_SOURCES += [ - 'nsIGonkTelephonyProvider.idl', - ] - EXTRA_COMPONENTS += [ - 'gonk/TelephonyProvider.js', - 'gonk/TelephonyProvider.manifest', - ] - FAIL_ON_WARNINGS = True LIBXUL_LIBRARY = True LIBRARY_NAME = 'domtelephony_s' + diff --git a/dom/telephony/nsIGonkTelephonyProvider.idl b/dom/telephony/nsIGonkTelephonyProvider.idl deleted file mode 100644 index 934f57d25c26..000000000000 --- a/dom/telephony/nsIGonkTelephonyProvider.idl +++ /dev/null @@ -1,31 +0,0 @@ -/* -*- Mode: idl; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 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/. */ - -#include "nsITelephonyProvider.idl" - -%{C++ -#define GONK_TELEPHONY_PROVIDER_CONTRACTID \ - "@mozilla.org/telephony/gonktelephonyprovider;1" -%} - -[scriptable, uuid(0d106c7e-ba47-48ee-ba48-c92002d401b6)] -interface nsIGonkTelephonyProvider : nsITelephonyProvider -{ - void notifyCallDisconnected(in jsval call); - - void notifyCallError(in long callIndex, - in AString error); - - void notifyCallRing(); - - void notifyCallStateChanged(in jsval call); - - void notifyCdmaCallWaiting(in AString number); - - void notifySupplementaryService(in long callIndex, - in AString notification); - - void notifyConferenceCallStateChanged(in unsigned short state); -}; diff --git a/dom/telephony/nsITelephonyProvider.idl b/dom/telephony/nsITelephonyProvider.idl index d8cb28adc703..8076ac3c5a4a 100644 --- a/dom/telephony/nsITelephonyProvider.idl +++ b/dom/telephony/nsITelephonyProvider.idl @@ -4,7 +4,7 @@ #include "nsISupports.idl" -[scriptable, uuid(3aa42e77-7c2b-43a1-b105-7be094b0817a)] +[scriptable, uuid(a5818719-e1b6-4fdc-8551-006055fa9996)] interface nsITelephonyListener : nsISupports { /** @@ -67,14 +67,15 @@ interface nsITelephonyListener : nsISupports * Indicates whether this call is outgoing or incoming. * @param isConference * Indicates whether this call is a conference call. + * @return true to continue enumeration or false to cancel. */ - void enumerateCallState(in unsigned long callIndex, - in unsigned short callState, - in AString number, - in boolean isActive, - in boolean isOutgoing, - in boolean isEmergency, - in boolean isConference); + boolean enumerateCallState(in unsigned long callIndex, + in unsigned short callState, + in AString number, + in boolean isActive, + in boolean isOutgoing, + in boolean isEmergency, + in boolean isConference); /** * Notify when RIL receives supplementary service notification. @@ -107,18 +108,11 @@ interface nsITelephonyListener : nsISupports void notifyCdmaCallWaiting(in AString number); }; -%{C++ -#define TELEPHONY_PROVIDER_CID \ - { 0x9cf8aa52, 0x7c1c, 0x4cde, { 0x97, 0x4e, 0xed, 0x2a, 0xa0, 0xe7, 0x35, 0xfa } } -#define TELEPHONY_PROVIDER_CONTRACTID \ - "@mozilla.org/telephony/telephonyprovider;1" -%} - /** * XPCOM component (in the content process) that provides the telephony * information. */ -[scriptable, uuid(effca006-1ca8-47f7-9bab-1323f90a14ec)] +[scriptable, uuid(45a2f856-4e07-499a-94c6-624f90c3345b)] interface nsITelephonyProvider : nsISupports { const unsigned short CALL_STATE_UNKNOWN = 0; @@ -141,8 +135,8 @@ interface nsITelephonyProvider : nsISupports * RadioInterfaceLayer in the chrome process. Only a content process that has * the 'telephony' permission is allowed to register. */ - void registerListener(in nsITelephonyListener listener); - void unregisterListener(in nsITelephonyListener listener); + void registerTelephonyMsg(in nsITelephonyListener listener); + void unregisterTelephonyMsg(in nsITelephonyListener listener); /** * Will continue calling listener.enumerateCallState until the listener @@ -153,8 +147,8 @@ interface nsITelephonyProvider : nsISupports /** * Functionality for making and managing phone calls. */ - void dial(in DOMString number, - in boolean isEmergency); + void dial(in DOMString number); + void dialEmergency(in DOMString number); void hangUp(in unsigned long callIndex); void startTone(in DOMString dtmfChar); diff --git a/dom/tests/mochitest/general/test_interfaces.html b/dom/tests/mochitest/general/test_interfaces.html index ca570fa076be..eaeda570150b 100644 --- a/dom/tests/mochitest/general/test_interfaces.html +++ b/dom/tests/mochitest/general/test_interfaces.html @@ -102,7 +102,7 @@ var interfaceNamesInGlobalScope = {name: "BluetoothStatusChangedEvent", b2g: true}, {name: "BoxObject", xbl: true}, {name: "BrowserFeedWriter", desktop: true}, - "CallEvent", + {name: "CallEvent", b2g: true}, "CameraCapabilities", "CameraControl", "CameraManager", @@ -531,9 +531,9 @@ var interfaceNamesInGlobalScope = "SVGViewElement", "SVGZoomAndPan", "SVGZoomEvent", - "Telephony", - "TelephonyCall", - "TelephonyCallGroup", + {name: "Telephony", b2g: true}, + {name: "TelephonyCall", b2g: true}, + {name: "TelephonyCallGroup", b2g: true}, "Text", "TextDecoder", "TextEncoder", diff --git a/dom/webidl/WebIDL.mk b/dom/webidl/WebIDL.mk index b5d7dafa9360..f18ce2bddd4b 100644 --- a/dom/webidl/WebIDL.mk +++ b/dom/webidl/WebIDL.mk @@ -34,8 +34,6 @@ webidl_files = \ BiquadFilterNode.webidl \ Blob.webidl \ BrowserElementDictionaries.webidl \ - CallEvent.webidl \ - CallsList.webidl \ CameraControl.webidl \ CameraManager.webidl \ CanvasRenderingContext2D.webidl \ @@ -361,9 +359,6 @@ webidl_files = \ SVGViewElement.webidl \ SVGZoomAndPan.webidl \ SVGZoomEvent.webidl \ - Telephony.webidl \ - TelephonyCall.webidl \ - TelephonyCallGroup.webidl \ Text.webidl \ TextDecoder.webidl \ TextEncoder.webidl \ @@ -450,8 +445,12 @@ endif ifdef MOZ_B2G_RIL webidl_files += \ + CallsList.webidl \ MozStkCommandEvent.webidl \ MozVoicemail.webidl \ + Telephony.webidl \ + TelephonyCall.webidl \ + TelephonyCallGroup.webidl \ $(NULL) endif @@ -496,6 +495,7 @@ endif ifdef MOZ_B2G_RIL webidl_files += \ + CallEvent.webidl \ CFStateChangeEvent.webidl \ DataErrorEvent.webidl \ IccCardLockErrorEvent.webidl \ diff --git a/gfx/gl/GLContext.cpp b/gfx/gl/GLContext.cpp index 7c5885153afb..04bf60573df4 100644 --- a/gfx/gl/GLContext.cpp +++ b/gfx/gl/GLContext.cpp @@ -493,7 +493,8 @@ GLContext::InitWithPrefix(const char *prefix, bool trygl) "Adreno (TM) 320", "PowerVR SGX 530", "PowerVR SGX 540", - "NVIDIA Tegra" + "NVIDIA Tegra", + "Android Emulator" }; mRenderer = RendererOther; @@ -1043,6 +1044,14 @@ GLContext::InitExtensions() MarkExtensionSupported(OES_EGL_sync); } + if (WorkAroundDriverBugs() && + Renderer() == RendererAndroidEmulator) { + // the Android emulator, which we use to run B2G reftests on, + // doesn't expose the OES_rgb8_rgba8 extension, but it seems to + // support it (tautologically, as it only runs on desktop GL). + MarkExtensionSupported(OES_rgb8_rgba8); + } + #ifdef DEBUG firstRun = false; #endif diff --git a/gfx/gl/GLContext.h b/gfx/gl/GLContext.h index 59cdc750139b..ce1a6a30e02a 100644 --- a/gfx/gl/GLContext.h +++ b/gfx/gl/GLContext.h @@ -153,6 +153,7 @@ public: RendererSGX530, RendererSGX540, RendererTegra, + RendererAndroidEmulator, RendererOther }; diff --git a/gfx/src/nsRegion.cpp b/gfx/src/nsRegion.cpp index cbf937be726d..c984de9e9088 100644 --- a/gfx/src/nsRegion.cpp +++ b/gfx/src/nsRegion.cpp @@ -246,9 +246,7 @@ void nsRegion::Init() mRectListHead.prev = mRectListHead.next = &mRectListHead; mCurRect = &mRectListHead; mRectCount = 0; - MOZ_ASSERT(mBoundRect.x == 0 && mBoundRect.y == 0 && - mBoundRect.width == 0 && mBoundRect.height == 0, - "Caller must have initialized mBoundRect"); + mBoundRect.SetRect (0, 0, 0, 0); } inline void nsRegion::InsertBefore (RgnRect* aNewRect, RgnRect* aRelativeRect) diff --git a/layout/base/nsDisplayList.cpp b/layout/base/nsDisplayList.cpp index 16076ce47de0..48e763c227e9 100644 --- a/layout/base/nsDisplayList.cpp +++ b/layout/base/nsDisplayList.cpp @@ -616,6 +616,7 @@ static void UnmarkFrameForDisplay(nsIFrame* aFrame) { static void RecordFrameMetrics(nsIFrame* aForFrame, nsIFrame* aScrollFrame, + const nsIFrame* aReferenceFrame, ContainerLayer* aRoot, const nsRect& aVisibleRect, const nsRect& aViewport, @@ -680,29 +681,26 @@ static void RecordFrameMetrics(nsIFrame* aForFrame, metrics.mMayHaveTouchListeners = aMayHaveTouchListeners; - if (aScrollFrame) { - metrics.mCompositionBounds = RoundedToInt(LayoutDeviceRect::FromAppUnits( - aScrollFrame->GetScreenRectInAppUnits(), auPerDevPixel) - * metrics.mResolution - * layerToScreenScale); + // Calculate the composition bounds as the size of the scroll frame and + // its origin relative to the reference frame. + // If aScrollFrame is null, we are in a document without a root scroll frame, + // so it's a xul document. In this case, use the size of the viewport frame. + nsIFrame* frameForCompositionBoundsCalculation = aScrollFrame ? aScrollFrame : aForFrame; + nsRect compositionBounds(frameForCompositionBoundsCalculation->GetOffsetToCrossDoc(aReferenceFrame), + frameForCompositionBoundsCalculation->GetSize()); + metrics.mCompositionBounds = RoundedToInt(LayoutDeviceRect::FromAppUnits(compositionBounds, auPerDevPixel) + * metrics.mResolution + * layerToScreenScale); - // Adjust for the size of scroll bars. - if (scrollableFrame && !LookAndFeel::GetInt(LookAndFeel::eIntID_UseOverlayScrollbars)) { - nsMargin sizes = scrollableFrame->GetActualScrollbarSizes(); - // Scrollbars are not subject to scaling, so CSS pixels = screen pixels for them. - ScreenIntMargin boundMargins(nsPresContext::AppUnitsToIntCSSPixels(sizes.top), - nsPresContext::AppUnitsToIntCSSPixels(sizes.right), - nsPresContext::AppUnitsToIntCSSPixels(sizes.bottom), - nsPresContext::AppUnitsToIntCSSPixels(sizes.left)); - metrics.mCompositionBounds.Deflate(boundMargins); - } - } else { - // We are in a document without a root scroll frame, so it's a xul document. - // In this case, use the size of the viewport frame. - metrics.mCompositionBounds = RoundedToInt(LayoutDeviceRect::FromAppUnits( - aForFrame->GetScreenRectInAppUnits(), auPerDevPixel) - * metrics.mResolution - * layerToScreenScale); + // Adjust composition bounds for the size of scroll bars. + if (scrollableFrame && !LookAndFeel::GetInt(LookAndFeel::eIntID_UseOverlayScrollbars)) { + nsMargin sizes = scrollableFrame->GetActualScrollbarSizes(); + // Scrollbars are not subject to scaling, so CSS pixels = screen pixels for them. + ScreenIntMargin boundMargins(nsPresContext::AppUnitsToIntCSSPixels(sizes.top), + nsPresContext::AppUnitsToIntCSSPixels(sizes.right), + nsPresContext::AppUnitsToIntCSSPixels(sizes.bottom), + nsPresContext::AppUnitsToIntCSSPixels(sizes.left)); + metrics.mCompositionBounds.Deflate(boundMargins); } metrics.mPresShellId = presShell->GetPresShellId(); @@ -1206,6 +1204,7 @@ void nsDisplayList::PaintForFrame(nsDisplayListBuilder* aBuilder, nsRect viewport(aBuilder->ToReferenceFrame(aForFrame), aForFrame->GetSize()); RecordFrameMetrics(aForFrame, rootScrollFrame, + aBuilder->FindReferenceFrameFor(aForFrame), root, mVisibleRect, viewport, (usingDisplayport ? &displayport : nullptr), (usingCriticalDisplayport ? &criticalDisplayport : nullptr), @@ -3287,7 +3286,8 @@ nsDisplayScrollLayer::BuildLayer(nsDisplayListBuilder* aBuilder, usingCriticalDisplayport = nsLayoutUtils::GetCriticalDisplayPort(content, &criticalDisplayport); } - RecordFrameMetrics(mScrolledFrame, mScrollFrame, layer, mVisibleRect, viewport, + RecordFrameMetrics(mScrolledFrame, mScrollFrame, ReferenceFrame(), layer, + mVisibleRect, viewport, (usingDisplayport ? &displayport : nullptr), (usingCriticalDisplayport ? &criticalDisplayport : nullptr), scrollId, aContainerParameters, false); diff --git a/layout/build/Makefile.in b/layout/build/Makefile.in index 51b8f933f90d..6cfafbacf958 100644 --- a/layout/build/Makefile.in +++ b/layout/build/Makefile.in @@ -59,7 +59,6 @@ SHARED_LIBRARY_LIBS = \ $(DEPTH)/dom/promise/$(LIB_PREFIX)dompromise_s.$(LIB_SUFFIX) \ $(DEPTH)/dom/src/notification/$(LIB_PREFIX)jsdomnotification_s.$(LIB_SUFFIX) \ $(DEPTH)/dom/system/$(LIB_PREFIX)domsystem_s.$(LIB_SUFFIX) \ - $(DEPTH)/dom/telephony/$(LIB_PREFIX)domtelephony_s.$(LIB_SUFFIX) \ $(DEPTH)/dom/workers/$(LIB_PREFIX)domworkers_s.$(LIB_SUFFIX) \ $(DEPTH)/dom/indexedDB/$(LIB_PREFIX)dom_indexeddb_s.$(LIB_SUFFIX) \ $(DEPTH)/dom/indexedDB/ipc/$(LIB_PREFIX)dom_indexeddb_ipc_s.$(LIB_SUFFIX) \ @@ -130,6 +129,7 @@ SHARED_LIBRARY_LIBS += $(DEPTH)/dom/camera/$(LIB_PREFIX)domcamera_s.$(LIB_SUFFIX ifdef MOZ_B2G_RIL #{ SHARED_LIBRARY_LIBS += \ $(DEPTH)/dom/system/gonk/$(LIB_PREFIX)domsystemgonk_s.$(LIB_SUFFIX) \ + $(DEPTH)/dom/telephony/$(LIB_PREFIX)domtelephony_s.$(LIB_SUFFIX) \ $(DEPTH)/dom/icc/src/$(LIB_PREFIX)dom_icc_s.$(LIB_SUFFIX) \ $(DEPTH)/dom/cellbroadcast/src/$(LIB_PREFIX)dom_cellbroadcast_s.$(LIB_SUFFIX) \ $(DEPTH)/dom/voicemail/$(LIB_PREFIX)domvoicemail_s.$(LIB_SUFFIX) \ diff --git a/layout/build/nsLayoutModule.cpp b/layout/build/nsLayoutModule.cpp index 6278d4b8352c..1d55dd374466 100644 --- a/layout/build/nsLayoutModule.cpp +++ b/layout/build/nsLayoutModule.cpp @@ -230,9 +230,6 @@ static void Shutdown(); #include "mozilla/dom/alarm/AlarmHalService.h" #include "mozilla/dom/time/TimeService.h" -#include "mozilla/dom/telephony/TelephonyFactory.h" -#include "nsITelephonyProvider.h" - #ifdef MOZ_WIDGET_GONK #include "GonkGPSGeolocationProvider.h" #endif @@ -241,7 +238,6 @@ static void Shutdown(); using namespace mozilla; using namespace mozilla::dom; using namespace mozilla::dom::mobilemessage; -using namespace mozilla::dom::telephony; using mozilla::dom::alarm::AlarmHalService; using mozilla::dom::indexedDB::IndexedDatabaseManager; using mozilla::dom::power::PowerManagerService; @@ -342,8 +338,6 @@ NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsVolumeService, #endif NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsIMediaManagerService, MediaManager::GetInstance) -NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsITelephonyProvider, - TelephonyFactory::CreateTelephonyProvider) //----------------------------------------------------------------------------- @@ -817,7 +811,6 @@ NS_DEFINE_NAMED_CID(NS_SYNTHVOICEREGISTRY_CID); #ifdef ACCESSIBILITY NS_DEFINE_NAMED_CID(NS_ACCESSIBILITY_SERVICE_CID); #endif -NS_DEFINE_NAMED_CID(TELEPHONY_PROVIDER_CID); static nsresult CreateWindowCommandTableConstructor(nsISupports *aOuter, @@ -1100,7 +1093,6 @@ static const mozilla::Module::CIDEntry kLayoutCIDs[] = { #ifdef ACCESSIBILITY { &kNS_ACCESSIBILITY_SERVICE_CID, false, NULL, CreateA11yService }, #endif - { &kTELEPHONY_PROVIDER_CID, false, NULL, nsITelephonyProviderConstructor }, { NULL } }; @@ -1256,7 +1248,6 @@ static const mozilla::Module::ContractIDEntry kLayoutContracts[] = { { "@mozilla.org/accessibilityService;1", &kNS_ACCESSIBILITY_SERVICE_CID }, { "@mozilla.org/accessibleRetrieval;1", &kNS_ACCESSIBILITY_SERVICE_CID }, #endif - { TELEPHONY_PROVIDER_CONTRACTID, &kTELEPHONY_PROVIDER_CID }, { NULL } }; diff --git a/mobile/android/base/BrowserApp.java b/mobile/android/base/BrowserApp.java index a44c76fc76ae..86ad24403a8c 100644 --- a/mobile/android/base/BrowserApp.java +++ b/mobile/android/base/BrowserApp.java @@ -13,6 +13,7 @@ import org.mozilla.gecko.gfx.GeckoLayerClient; import org.mozilla.gecko.gfx.ImmutableViewportMetrics; import org.mozilla.gecko.gfx.LayerView; import org.mozilla.gecko.gfx.PanZoomController; +import org.mozilla.gecko.health.BrowserHealthRecorder; import org.mozilla.gecko.health.BrowserHealthReporter; import org.mozilla.gecko.home.BrowserSearch; import org.mozilla.gecko.home.HomePager; @@ -1464,6 +1465,8 @@ abstract public class BrowserApp extends GeckoApp return; } + recordSearch(null, "barkeyword"); + // Otherwise, construct a search query from the bookmark keyword. final String searchUrl = keywordUrl.replace("%s", URLEncoder.encode(keywordSearch)); Tabs.getInstance().loadUrl(searchUrl, Tabs.LOADURL_USER_ENTERED); @@ -1471,6 +1474,28 @@ abstract public class BrowserApp extends GeckoApp }); } + /** + * Record in Health Report that a search has occurred. + * + * @param identifier + * a search identifier, such as "partnername". Can be null. + * @param where + * where the search was initialized; one of the values in + * {@link BrowserHealthRecorder#SEARCH_LOCATIONS}. + */ + private static void recordSearch(String identifier, String where) { + Log.i(LOGTAG, "Recording search: " + identifier + ", " + where); + try { + JSONObject message = new JSONObject(); + message.put("type", BrowserHealthRecorder.EVENT_SEARCH); + message.put("location", where); + message.put("identifier", identifier); + GeckoAppShell.getEventDispatcher().dispatchEvent(message); + } catch (Exception e) { + Log.w(LOGTAG, "Error recording search.", e); + } + } + boolean dismissEditingMode() { if (!mBrowserToolbar.isEditing()) { return false; @@ -2209,6 +2234,7 @@ abstract public class BrowserApp extends GeckoApp // BrowserSearch.OnSearchListener @Override public void onSearch(String engineId, String text) { + recordSearch(engineId, "barsuggest"); openUrl(text, engineId); } diff --git a/mobile/android/base/BrowserToolbar.java b/mobile/android/base/BrowserToolbar.java index 857963869175..1f1cf828c663 100644 --- a/mobile/android/base/BrowserToolbar.java +++ b/mobile/android/base/BrowserToolbar.java @@ -861,11 +861,11 @@ public class BrowserToolbar extends GeckoRelativeLayout return; } - // If toolbar is in edit mode, this means the entry is expanded and the - // tabs button is translated offscreen. Don't trigger tabs counter + // If toolbar is in edit mode on a phone, this means the entry is expanded + // and the tabs button is translated offscreen. Don't trigger tabs counter // updates until the tabs button is back on screen. // See stopEditing() - if (!isEditing()) { + if (!isEditing() || HardwareUtils.isTablet()) { mTabsCounter.setCount(count); mTabs.setContentDescription((count > 1) ? @@ -875,11 +875,11 @@ public class BrowserToolbar extends GeckoRelativeLayout } public void updateTabCount(int count) { - // If toolbar is in edit mode, this means the entry is expanded and the - // tabs button is translated offscreen. Don't trigger tabs counter + // If toolbar is in edit mode on a phone, this means the entry is expanded + // and the tabs button is translated offscreen. Don't trigger tabs counter // updates until the tabs button is back on screen. // See stopEditing() - if (isEditing()) { + if (isEditing() && !HardwareUtils.isTablet()) { return; } @@ -1378,9 +1378,10 @@ public class BrowserToolbar extends GeckoRelativeLayout if (HardwareUtils.isTablet() || Build.VERSION.SDK_INT < 11) { hideUrlEditContainer(); - updateTabCountAndAnimate(Tabs.getInstance().getDisplayCount()); if (!HardwareUtils.isTablet()) { + updateTabCountAndAnimate(Tabs.getInstance().getDisplayCount()); + if (mUrlBarRightEdge != null) { ViewHelper.setTranslationX(mUrlBarRightEdge, 0); } diff --git a/mobile/android/chrome/content/browser.js b/mobile/android/chrome/content/browser.js index 7eaab9052b54..83f05ec2dc6d 100644 --- a/mobile/android/chrome/content/browser.js +++ b/mobile/android/chrome/content/browser.js @@ -334,7 +334,6 @@ var BrowserApp = { Reader.init(); UserAgentOverrides.init(); DesktopUserAgent.init(); - ExternalApps.init(); Distribution.init(); Tabs.init(); #ifdef ACCESSIBILITY @@ -368,6 +367,9 @@ var BrowserApp = { SearchEngines.init(); this.initContextMenu(); } + // The order that context menu items are added is important + // Make sure the "Open in App" context menu item appears at the bottom of the list + ExternalApps.init(); // XXX maybe we don't do this if the launch was kicked off from external Services.io.offline = false; diff --git a/security/sandbox/seccomp_filter.h b/security/sandbox/seccomp_filter.h index 4e6a629ec946..798fff644885 100644 --- a/security/sandbox/seccomp_filter.h +++ b/security/sandbox/seccomp_filter.h @@ -69,18 +69,24 @@ ALLOW_SYSCALL(munmap), \ ALLOW_SYSCALL(mmap2), \ ALLOW_SYSCALL(mprotect), \ + ALLOW_SYSCALL(dup), \ + ALLOW_SYSCALL(getuid32), \ + ALLOW_SYSCALL(nanosleep), \ /* Must remove all of the following in the future, when no longer used */ \ /* open() is for some legacy APIs such as font loading. */ \ + /* See bug 906996 for removing unlink(). */ \ ALLOW_SYSCALL(open), \ ALLOW_SYSCALL(fstat64), \ ALLOW_SYSCALL(stat64), \ ALLOW_SYSCALL(prctl), \ ALLOW_SYSCALL(access), \ ALLOW_SYSCALL(getdents64), \ + ALLOW_SYSCALL(unlink), \ /* Should remove all of the following in the future, if possible */ \ ALLOW_SYSCALL(getpriority), \ ALLOW_SYSCALL(setpriority), \ ALLOW_SYSCALL(sigprocmask), \ + ALLOW_SYSCALL(sched_setscheduler), \ /* Always last and always OK calls */ \ SECCOMP_WHITELIST_ADD \ /* restart_syscall is called internally, generally when debugging */ \ diff --git a/toolkit/devtools/server/actors/webapps.js b/toolkit/devtools/server/actors/webapps.js index 6185914f796d..00799b30530c 100644 --- a/toolkit/devtools/server/actors/webapps.js +++ b/toolkit/devtools/server/actors/webapps.js @@ -607,10 +607,11 @@ WebappsActor.prototype = { } let defer = promise.defer(); - let reg = DOMApplicationRegistry; - reg.launch( + + DOMApplicationRegistry.launch( aRequest.manifestURL, aRequest.startPoint || "", + Date.now(), function onsuccess() { defer.resolve({}); }, diff --git a/widget/gonk/Makefile.in b/widget/gonk/Makefile.in index eac18d69fcaa..7356b43ceac4 100644 --- a/widget/gonk/Makefile.in +++ b/widget/gonk/Makefile.in @@ -28,6 +28,7 @@ DEFINES += -DHAVE_OFF64_T -DSK_BUILD_FOR_ANDROID_NDK LOCAL_INCLUDES += \ -I$(ANDROID_SOURCE)/hardware/libhardware/include \ -I$(ANDROID_SOURCE)/hardware/libhardware_legacy/include \ + -I$(ANDROID_SOURCE)/frameworks/native/opengl/include \ -I$(topsrcdir)/widget/xpwidgets \ -I$(topsrcdir)/widget/shared \ -I$(topsrcdir)/dom/system/android \ diff --git a/widget/gonk/libui/BitSet.h b/widget/gonk/libui/BitSet.h deleted file mode 100644 index 9452e86d6175..000000000000 --- a/widget/gonk/libui/BitSet.h +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef UTILS_BITSET_H -#define UTILS_BITSET_H - -#include - -/* - * Contains some bit manipulation helpers. - */ - -namespace android { - -// A simple set of 32 bits that can be individually marked or cleared. -struct BitSet32 { - uint32_t value; - - inline BitSet32() : value(0) { } - explicit inline BitSet32(uint32_t value) : value(value) { } - - // Gets the value associated with a particular bit index. - static inline uint32_t valueForBit(uint32_t n) { return 0x80000000 >> n; } - - // Clears the bit set. - inline void clear() { value = 0; } - - // Returns the number of marked bits in the set. - inline uint32_t count() const { return __builtin_popcount(value); } - - // Returns true if the bit set does not contain any marked bits. - inline bool isEmpty() const { return ! value; } - - // Returns true if the bit set does not contain any unmarked bits. - inline bool isFull() const { return value == 0xffffffff; } - - // Returns true if the specified bit is marked. - inline bool hasBit(uint32_t n) const { return value & valueForBit(n); } - - // Marks the specified bit. - inline void markBit(uint32_t n) { value |= valueForBit(n); } - - // Clears the specified bit. - inline void clearBit(uint32_t n) { value &= ~ valueForBit(n); } - - // Finds the first marked bit in the set. - // Result is undefined if all bits are unmarked. - inline uint32_t firstMarkedBit() const { return __builtin_clz(value); } - - // Finds the first unmarked bit in the set. - // Result is undefined if all bits are marked. - inline uint32_t firstUnmarkedBit() const { return __builtin_clz(~ value); } - - // Finds the last marked bit in the set. - // Result is undefined if all bits are unmarked. - inline uint32_t lastMarkedBit() const { return 31 - __builtin_ctz(value); } - - // Finds the first marked bit in the set and clears it. Returns the bit index. - // Result is undefined if all bits are unmarked. - inline uint32_t clearFirstMarkedBit() { - uint32_t n = firstMarkedBit(); - clearBit(n); - return n; - } - - // Finds the first unmarked bit in the set and marks it. Returns the bit index. - // Result is undefined if all bits are marked. - inline uint32_t markFirstUnmarkedBit() { - uint32_t n = firstUnmarkedBit(); - markBit(n); - return n; - } - - // Finds the last marked bit in the set and clears it. Returns the bit index. - // Result is undefined if all bits are unmarked. - inline uint32_t clearLastMarkedBit() { - uint32_t n = lastMarkedBit(); - clearBit(n); - return n; - } - - // Gets the index of the specified bit in the set, which is the number of - // marked bits that appear before the specified bit. - inline uint32_t getIndexOfBit(uint32_t n) const { - return __builtin_popcount(value & ~(0xffffffffUL >> n)); - } - - inline bool operator== (const BitSet32& other) const { return value == other.value; } - inline bool operator!= (const BitSet32& other) const { return value != other.value; } -}; - -} // namespace android - -#endif // UTILS_BITSET_H diff --git a/widget/gonk/libui/DisplayInfo.h b/widget/gonk/libui/DisplayInfo.h deleted file mode 100644 index 5774ec0ef424..000000000000 --- a/widget/gonk/libui/DisplayInfo.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -#ifndef ANDROID_UI_DISPLAY_INFO_H -#define ANDROID_UI_DISPLAY_INFO_H - -#include -#include - -#include "PixelFormat.h" - -namespace android { - -struct DisplayInfo { - uint32_t w; - uint32_t h; - PixelFormatInfo pixelFormatInfo; - uint8_t orientation; - uint8_t reserved[3]; - float fps; - float density; - float xdpi; - float ydpi; -}; - -/* Display orientations as defined in Surface.java and ISurfaceComposer.h. */ -enum { - DISPLAY_ORIENTATION_0 = 0, - DISPLAY_ORIENTATION_90 = 1, - DISPLAY_ORIENTATION_180 = 2, - DISPLAY_ORIENTATION_270 = 3 -}; - - -}; // namespace android - -#endif // ANDROID_COMPOSER_DISPLAY_INFO_H - diff --git a/widget/gonk/libui/EventHub.cpp b/widget/gonk/libui/EventHub.cpp index eccbf3dc715d..89581bcef014 100644 --- a/widget/gonk/libui/EventHub.cpp +++ b/widget/gonk/libui/EventHub.cpp @@ -17,14 +17,15 @@ #define LOG_TAG "EventHub" // #define LOG_NDEBUG 0 +#include "cutils_log.h" -#include "utils_Log.h" #include "EventHub.h" #include #include -#include "Timers.h" +#include "cutils_log.h" +#include #include #include @@ -48,6 +49,7 @@ #include #include #include +#include /* this macro is used to tell if "bit" is set in "array" * it selects a byte from the array, and does a boolean AND @@ -78,6 +80,48 @@ static inline const char* toString(bool value) { return value ? "true" : "false"; } +static String8 sha1(const String8& in) { + SHA1_CTX ctx; + SHA1Init(&ctx); + SHA1Update(&ctx, reinterpret_cast(in.string()), in.size()); + u_char digest[SHA1_DIGEST_LENGTH]; + SHA1Final(digest, &ctx); + + String8 out; + for (size_t i = 0; i < SHA1_DIGEST_LENGTH; i++) { + out.appendFormat("%02x", digest[i]); + } + return out; +} + +static void setDescriptor(InputDeviceIdentifier& identifier) { + // Compute a device descriptor that uniquely identifies the device. + // The descriptor is assumed to be a stable identifier. Its value should not + // change between reboots, reconnections, firmware updates or new releases of Android. + // Ideally, we also want the descriptor to be short and relatively opaque. + String8 rawDescriptor; + rawDescriptor.appendFormat(":%04x:%04x:", identifier.vendor, identifier.product); + if (!identifier.uniqueId.isEmpty()) { + rawDescriptor.append("uniqueId:"); + rawDescriptor.append(identifier.uniqueId); + } if (identifier.vendor == 0 && identifier.product == 0) { + // If we don't know the vendor and product id, then the device is probably + // built-in so we need to rely on other information to uniquely identify + // the input device. Usually we try to avoid relying on the device name or + // location but for built-in input device, they are unlikely to ever change. + if (!identifier.name.isEmpty()) { + rawDescriptor.append("name:"); + rawDescriptor.append(identifier.name); + } else if (!identifier.location.isEmpty()) { + rawDescriptor.append("location:"); + rawDescriptor.append(identifier.location); + } + } + identifier.descriptor = sha1(rawDescriptor); + ALOGV("Created descriptor: raw=%s, cooked=%s", rawDescriptor.string(), + identifier.descriptor.string()); +} + // --- Global Functions --- uint32_t getAbsAxisUsage(int32_t axis, uint32_t deviceClasses) { @@ -118,12 +162,15 @@ EventHub::Device::Device(int fd, int32_t id, const String8& path, const InputDeviceIdentifier& identifier) : next(NULL), fd(fd), id(id), path(path), identifier(identifier), - classes(0), configuration(NULL), virtualKeyMap(NULL) { + classes(0), configuration(NULL), virtualKeyMap(NULL), + ffEffectPlaying(false), ffEffectId(-1), + timestampOverrideSec(0), timestampOverrideUsec(0) { memset(keyBitmask, 0, sizeof(keyBitmask)); memset(absBitmask, 0, sizeof(absBitmask)); memset(relBitmask, 0, sizeof(relBitmask)); memset(swBitmask, 0, sizeof(swBitmask)); memset(ledBitmask, 0, sizeof(ledBitmask)); + memset(ffBitmask, 0, sizeof(ffBitmask)); memset(propBitmask, 0, sizeof(propBitmask)); } @@ -149,15 +196,13 @@ const int EventHub::EPOLL_SIZE_HINT; const int EventHub::EPOLL_MAX_EVENTS; EventHub::EventHub(void) : - mBuiltInKeyboardId(-1), mNextDeviceId(1), + mBuiltInKeyboardId(NO_BUILT_IN_KEYBOARD), mNextDeviceId(1), mOpeningDevices(0), mClosingDevices(0), mNeedToSendFinishedDeviceScan(false), mNeedToReopenDevices(false), mNeedToScanDevices(true), mPendingEventCount(0), mPendingEventIndex(0), mPendingINotify(false) { acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID); - mNumCpus = sysconf(_SC_NPROCESSORS_ONLN); - mEpollFd = epoll_create(EPOLL_SIZE_HINT); LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance. errno=%d", errno); @@ -211,11 +256,11 @@ EventHub::~EventHub(void) { release_wake_lock(WAKE_LOCK_ID); } -String8 EventHub::getDeviceName(int32_t deviceId) const { +InputDeviceIdentifier EventHub::getDeviceIdentifier(int32_t deviceId) const { AutoMutex _l(mLock); Device* device = getDeviceLocked(deviceId); - if (device == NULL) return String8(); - return device->identifier.name; + if (device == NULL) return InputDeviceIdentifier(); + return device->identifier; } uint32_t EventHub::getDeviceClasses(int32_t deviceId) const { @@ -243,7 +288,7 @@ status_t EventHub::getAbsoluteAxisInfo(int32_t deviceId, int axis, AutoMutex _l(mLock); Device* device = getDeviceLocked(deviceId); - if (device && test_bit(axis, device->absBitmask)) { + if (device && !device->isVirtual() && test_bit(axis, device->absBitmask)) { struct input_absinfo info; if(ioctl(device->fd, EVIOCGABS(axis), &info)) { ALOGW("Error reading absolute controller %d for device %s fd %d, errno=%d", @@ -294,7 +339,7 @@ int32_t EventHub::getScanCodeState(int32_t deviceId, int32_t scanCode) const { AutoMutex _l(mLock); Device* device = getDeviceLocked(deviceId); - if (device && test_bit(scanCode, device->keyBitmask)) { + if (device && !device->isVirtual() && test_bit(scanCode, device->keyBitmask)) { uint8_t keyState[sizeof_bit_array(KEY_MAX + 1)]; memset(keyState, 0, sizeof(keyState)); if (ioctl(device->fd, EVIOCGKEY(sizeof(keyState)), keyState) >= 0) { @@ -309,7 +354,7 @@ int32_t EventHub::getKeyCodeState(int32_t deviceId, int32_t keyCode) const { AutoMutex _l(mLock); Device* device = getDeviceLocked(deviceId); - if (device && device->keyMap.haveKeyLayout()) { + if (device && !device->isVirtual() && device->keyMap.haveKeyLayout()) { Vector scanCodes; device->keyMap.keyLayoutMap->findScanCodesForKey(keyCode, &scanCodes); if (scanCodes.size() != 0) { @@ -334,7 +379,7 @@ int32_t EventHub::getSwitchState(int32_t deviceId, int32_t sw) const { AutoMutex _l(mLock); Device* device = getDeviceLocked(deviceId); - if (device && test_bit(sw, device->swBitmask)) { + if (device && !device->isVirtual() && test_bit(sw, device->swBitmask)) { uint8_t swState[sizeof_bit_array(SW_MAX + 1)]; memset(swState, 0, sizeof(swState)); if (ioctl(device->fd, EVIOCGSW(sizeof(swState)), swState) >= 0) { @@ -352,7 +397,7 @@ status_t EventHub::getAbsoluteAxisValue(int32_t deviceId, int32_t axis, int32_t* AutoMutex _l(mLock); Device* device = getDeviceLocked(deviceId); - if (device && test_bit(axis, device->absBitmask)) { + if (device && !device->isVirtual() && test_bit(axis, device->absBitmask)) { struct input_absinfo info; if(ioctl(device->fd, EVIOCGABS(axis), &info)) { ALOGW("Error reading absolute controller %d for device %s fd %d, errno=%d", @@ -395,58 +440,46 @@ bool EventHub::markSupportedKeyCodes(int32_t deviceId, size_t numCodes, return false; } -status_t EventHub::mapKey(int32_t deviceId, int scancode, - int32_t* outKeycode, uint32_t* outFlags) const -{ +status_t EventHub::mapKey(int32_t deviceId, int32_t scanCode, int32_t usageCode, + int32_t* outKeycode, uint32_t* outFlags) const { AutoMutex _l(mLock); Device* device = getDeviceLocked(deviceId); - - if (device && device->keyMap.haveKeyLayout()) { - status_t err = device->keyMap.keyLayoutMap->mapKey(scancode, outKeycode, outFlags); - if (err == NO_ERROR) { - return NO_ERROR; + + if (device) { + // Check the key character map first. + sp kcm = device->getKeyCharacterMap(); + if (kcm != NULL) { + if (!kcm->mapKey(scanCode, usageCode, outKeycode)) { + *outFlags = 0; + return NO_ERROR; + } } - } - - if (mBuiltInKeyboardId != -1) { - device = getDeviceLocked(mBuiltInKeyboardId); - - if (device && device->keyMap.haveKeyLayout()) { - status_t err = device->keyMap.keyLayoutMap->mapKey(scancode, outKeycode, outFlags); - if (err == NO_ERROR) { + + // Check the key layout next. + if (device->keyMap.haveKeyLayout()) { + if (!device->keyMap.keyLayoutMap->mapKey( + scanCode, usageCode, outKeycode, outFlags)) { return NO_ERROR; } } } - + *outKeycode = 0; *outFlags = 0; return NAME_NOT_FOUND; } -status_t EventHub::mapAxis(int32_t deviceId, int scancode, AxisInfo* outAxisInfo) const -{ +status_t EventHub::mapAxis(int32_t deviceId, int32_t scanCode, AxisInfo* outAxisInfo) const { AutoMutex _l(mLock); Device* device = getDeviceLocked(deviceId); if (device && device->keyMap.haveKeyLayout()) { - status_t err = device->keyMap.keyLayoutMap->mapAxis(scancode, outAxisInfo); + status_t err = device->keyMap.keyLayoutMap->mapAxis(scanCode, outAxisInfo); if (err == NO_ERROR) { return NO_ERROR; } } - if (mBuiltInKeyboardId != -1) { - device = getDeviceLocked(mBuiltInKeyboardId); - - if (device && device->keyMap.haveKeyLayout()) { - status_t err = device->keyMap.keyLayoutMap->mapAxis(scancode, outAxisInfo); - if (err == NO_ERROR) { - return NO_ERROR; - } - } - } - return NAME_NOT_FOUND; } @@ -481,7 +514,7 @@ bool EventHub::hasLed(int32_t deviceId, int32_t led) const { void EventHub::setLedState(int32_t deviceId, int32_t led, bool on) { AutoMutex _l(mLock); Device* device = getDeviceLocked(deviceId); - if (device && led >= 0 && led <= LED_MAX) { + if (device && !device->isVirtual() && led >= 0 && led <= LED_MAX) { struct input_event ev; ev.time.tv_sec = 0; ev.time.tv_usec = 0; @@ -507,17 +540,88 @@ void EventHub::getVirtualKeyDefinitions(int32_t deviceId, } } -String8 EventHub::getKeyCharacterMapFile(int32_t deviceId) const { +sp EventHub::getKeyCharacterMap(int32_t deviceId) const { AutoMutex _l(mLock); Device* device = getDeviceLocked(deviceId); if (device) { - return device->keyMap.keyCharacterMapFile; + return device->getKeyCharacterMap(); + } + return NULL; +} + +bool EventHub::setKeyboardLayoutOverlay(int32_t deviceId, + const sp& map) { + AutoMutex _l(mLock); + Device* device = getDeviceLocked(deviceId); + if (device) { + if (map != device->overlayKeyMap) { + device->overlayKeyMap = map; + device->combinedKeyMap = KeyCharacterMap::combine( + device->keyMap.keyCharacterMap, map); + return true; + } + } + return false; +} + +void EventHub::vibrate(int32_t deviceId, nsecs_t duration) { + AutoMutex _l(mLock); + Device* device = getDeviceLocked(deviceId); + if (device && !device->isVirtual()) { + ff_effect effect; + memset(&effect, 0, sizeof(effect)); + effect.type = FF_RUMBLE; + effect.id = device->ffEffectId; + effect.u.rumble.strong_magnitude = 0xc000; + effect.u.rumble.weak_magnitude = 0xc000; + effect.replay.length = (duration + 999999LL) / 1000000LL; + effect.replay.delay = 0; + if (ioctl(device->fd, EVIOCSFF, &effect)) { + ALOGW("Could not upload force feedback effect to device %s due to error %d.", + device->identifier.name.string(), errno); + return; + } + device->ffEffectId = effect.id; + + struct input_event ev; + ev.time.tv_sec = 0; + ev.time.tv_usec = 0; + ev.type = EV_FF; + ev.code = device->ffEffectId; + ev.value = 1; + if (write(device->fd, &ev, sizeof(ev)) != sizeof(ev)) { + ALOGW("Could not start force feedback effect on device %s due to error %d.", + device->identifier.name.string(), errno); + return; + } + device->ffEffectPlaying = true; + } +} + +void EventHub::cancelVibrate(int32_t deviceId) { + AutoMutex _l(mLock); + Device* device = getDeviceLocked(deviceId); + if (device && !device->isVirtual()) { + if (device->ffEffectPlaying) { + device->ffEffectPlaying = false; + + struct input_event ev; + ev.time.tv_sec = 0; + ev.time.tv_usec = 0; + ev.type = EV_FF; + ev.code = device->ffEffectId; + ev.value = 0; + if (write(device->fd, &ev, sizeof(ev)) != sizeof(ev)) { + ALOGW("Could not stop force feedback effect on device %s due to error %d.", + device->identifier.name.string(), errno); + return; + } + } } - return String8(); } EventHub::Device* EventHub::getDeviceLocked(int32_t deviceId) const { - if (deviceId == 0) { + if (deviceId == BUILT_IN_KEYBOARD_ID) { deviceId = mBuiltInKeyboardId; } ssize_t index = mDevices.indexOfKey(deviceId); @@ -565,7 +669,7 @@ size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSiz device->id, device->path.string()); mClosingDevices = device->next; event->when = now; - event->deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id; + event->deviceId = device->id == mBuiltInKeyboardId ? BUILT_IN_KEYBOARD_ID : device->id; event->type = DEVICE_REMOVED; event += 1; delete device; @@ -648,8 +752,9 @@ size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSiz sizeof(struct input_event) * capacity); if (readSize == 0 || (readSize < 0 && errno == ENODEV)) { // Device was removed before INotify noticed. - ALOGW("could not get event, removed? (fd: %d size: %d bufferSize: %d capacity: %d errno: %d)\n", - device->fd, readSize, bufferSize, capacity, errno); + ALOGW("could not get event, removed? (fd: %d size: %d bufferSize: %d " + "capacity: %d errno: %d)\n", + device->fd, readSize, bufferSize, capacity, errno); deviceChanged = true; closeDeviceLocked(device); } else if (readSize < 0) { @@ -663,12 +768,37 @@ size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSiz size_t count = size_t(readSize) / sizeof(struct input_event); for (size_t i = 0; i < count; i++) { - const struct input_event& iev = readBuffer[i]; - ALOGV("%s got: t0=%d, t1=%d, type=%d, code=%d, value=%d", + struct input_event& iev = readBuffer[i]; + ALOGV("%s got: time=%d.%06d, type=%d, code=%d, value=%d", device->path.string(), (int) iev.time.tv_sec, (int) iev.time.tv_usec, iev.type, iev.code, iev.value); + // Some input devices may have a better concept of the time + // when an input event was actually generated than the kernel + // which simply timestamps all events on entry to evdev. + // This is a custom Android extension of the input protocol + // mainly intended for use with uinput based device drivers. + if (iev.type == EV_MSC) { + if (iev.code == MSC_ANDROID_TIME_SEC) { + device->timestampOverrideSec = iev.value; + continue; + } else if (iev.code == MSC_ANDROID_TIME_USEC) { + device->timestampOverrideUsec = iev.value; + continue; + } + } + if (device->timestampOverrideSec || device->timestampOverrideUsec) { + iev.time.tv_sec = device->timestampOverrideSec; + iev.time.tv_usec = device->timestampOverrideUsec; + if (iev.type == EV_SYN && iev.code == SYN_REPORT) { + device->timestampOverrideSec = 0; + device->timestampOverrideUsec = 0; + } + ALOGV("applied override time %d.%06d", + int(iev.time.tv_sec), int(iev.time.tv_usec)); + } + #ifdef HAVE_POSIX_CLOCKS // Use the time specified in the event instead of the current time // so that downstream code can get more accurate estimates of @@ -684,24 +814,50 @@ size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSiz event->when = nsecs_t(iev.time.tv_sec) * 1000000000LL + nsecs_t(iev.time.tv_usec) * 1000LL; ALOGV("event time %lld, now %lld", event->when, now); + + // Bug 7291243: Add a guard in case the kernel generates timestamps + // that appear to be far into the future because they were generated + // using the wrong clock source. + // + // This can happen because when the input device is initially opened + // it has a default clock source of CLOCK_REALTIME. Any input events + // enqueued right after the device is opened will have timestamps + // generated using CLOCK_REALTIME. We later set the clock source + // to CLOCK_MONOTONIC but it is already too late. + // + // Invalid input event timestamps can result in ANRs, crashes and + // and other issues that are hard to track down. We must not let them + // propagate through the system. + // + // Log a warning so that we notice the problem and recover gracefully. + if (event->when >= now + 10 * 1000000000LL) { + // Double-check. Time may have moved on. + nsecs_t time = systemTime(SYSTEM_TIME_MONOTONIC); + if (event->when > time) { + ALOGW("An input event from %s has a timestamp that appears to " + "have been generated using the wrong clock source " + "(expected CLOCK_MONOTONIC): " + "event time %lld, current time %lld, call time %lld. " + "Using current time instead.", + device->path.string(), event->when, time, now); + event->when = time; + } else { + ALOGV("Event time is ok but failed the fast path and required " + "an extra call to systemTime: " + "event time %lld, current time %lld, call time %lld.", + event->when, time, now); + } + } #else event->when = now; #endif event->deviceId = deviceId; event->type = iev.type; - event->scanCode = iev.code; + event->code = iev.code; event->value = iev.value; - event->keyCode = AKEYCODE_UNKNOWN; - event->flags = 0; - if (iev.type == EV_KEY && device->keyMap.haveKeyLayout()) { - status_t err = device->keyMap.keyLayoutMap->mapKey(iev.code, - &event->keyCode, &event->flags); - ALOGV("iev.code=%d keyCode=%d flags=0x%08x err=%d\n", - iev.code, event->keyCode, event->flags, err); - } event += 1; + capacity -= 1; } - capacity -= count; if (capacity == 0) { // The result buffer is full. Reset the pending event index // so we will try to read the device again on the next iteration. @@ -709,6 +865,11 @@ size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSiz break; } } + } else if (eventItem.events & EPOLLHUP) { + ALOGI("Removing device %s due to epoll hang-up event.", + device->identifier.name.string()); + deviceChanged = true; + closeDeviceLocked(device); } else { ALOGW("Received unexpected epoll event 0x%08x for device %s.", eventItem.events, device->identifier.name.string()); @@ -774,19 +935,6 @@ size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSiz } else { // Some events occurred. mPendingEventCount = size_t(pollResult); - - // On an SMP system, it is possible for the framework to read input events - // faster than the kernel input device driver can produce a complete packet. - // Because poll() wakes up as soon as the first input event becomes available, - // the framework will often end up reading one event at a time until the - // packet is complete. Instead of one call to read() returning 71 events, - // it could take 71 calls to read() each returning 1 event. - // - // Sleep for a short period of time after waking up from the poll() to give - // the kernel time to finish writing the entire packet of input events. - if (mNumCpus > 1) { - usleep(250); - } } } @@ -812,6 +960,9 @@ void EventHub::scanDevicesLocked() { if(res < 0) { ALOGE("scan dir failed for %s\n", DEVICE_PATH); } + if (mDevices.indexOfKey(VIRTUAL_KEYBOARD_ID) < 0) { + createVirtualKeyboardLocked(); + } } // ---------------------------------------------------------------------------- @@ -845,7 +996,7 @@ status_t EventHub::openDeviceLocked(const char *devicePath) { ALOGV("Opening device: %s", devicePath); - int fd = open(devicePath, O_RDWR); + int fd = open(devicePath, O_RDWR | O_CLOEXEC); if(fd < 0) { ALOGE("could not open %s, %s\n", devicePath, strerror(errno)); return -1; @@ -907,6 +1058,9 @@ status_t EventHub::openDeviceLocked(const char *devicePath) { identifier.uniqueId.setTo(buffer); } + // Fill in the descriptor. + setDescriptor(identifier); + // Make file descriptor non-blocking for use with poll(). if (fcntl(fd, F_SETFL, O_NONBLOCK)) { ALOGE("Error %d making device file descriptor non-blocking.", errno); @@ -918,19 +1072,18 @@ status_t EventHub::openDeviceLocked(const char *devicePath) { int32_t deviceId = mNextDeviceId++; Device* device = new Device(fd, deviceId, String8(devicePath), identifier); -#if 0 - ALOGI("add device %d: %s\n", deviceId, devicePath); - ALOGI(" bus: %04x\n" - " vendor %04x\n" - " product %04x\n" - " version %04x\n", + ALOGV("add device %d: %s\n", deviceId, devicePath); + ALOGV(" bus: %04x\n" + " vendor %04x\n" + " product %04x\n" + " version %04x\n", identifier.bus, identifier.vendor, identifier.product, identifier.version); - ALOGI(" name: \"%s\"\n", identifier.name.string()); - ALOGI(" location: \"%s\"\n", identifier.location.string()); - ALOGI(" unique id: \"%s\"\n", identifier.uniqueId.string()); - ALOGI(" driver: v%d.%d.%d\n", + ALOGV(" name: \"%s\"\n", identifier.name.string()); + ALOGV(" location: \"%s\"\n", identifier.location.string()); + ALOGV(" unique id: \"%s\"\n", identifier.uniqueId.string()); + ALOGV(" descriptor: \"%s\"\n", identifier.descriptor.string()); + ALOGV(" driver: v%d.%d.%d\n", driverVersion >> 16, (driverVersion >> 8) & 0xff, driverVersion & 0xff); -#endif // Load the configuration file for the device. loadConfigurationLocked(device); @@ -941,6 +1094,7 @@ status_t EventHub::openDeviceLocked(const char *devicePath) { ioctl(fd, EVIOCGBIT(EV_REL, sizeof(device->relBitmask)), device->relBitmask); ioctl(fd, EVIOCGBIT(EV_SW, sizeof(device->swBitmask)), device->swBitmask); ioctl(fd, EVIOCGBIT(EV_LED, sizeof(device->ledBitmask)), device->ledBitmask); + ioctl(fd, EVIOCGBIT(EV_FF, sizeof(device->ffBitmask)), device->ffBitmask); ioctl(fd, EVIOCGPROP(sizeof(device->propBitmask)), device->propBitmask); // See if this is a keyboard. Ignore everything in the button range except for @@ -970,10 +1124,9 @@ status_t EventHub::openDeviceLocked(const char *devicePath) { // Some joysticks such as the PS3 controller report axes that conflict // with the ABS_MT range. Try to confirm that the device really is // a touch screen. - // Mozilla Bug 741038 - support GB touchscreen drivers - //if (test_bit(BTN_TOUCH, device->keyBitmask) || !haveGamepadButtons) { + if (test_bit(BTN_TOUCH, device->keyBitmask) || !haveGamepadButtons) { device->classes |= INPUT_DEVICE_CLASS_TOUCH | INPUT_DEVICE_CLASS_TOUCH_MT; - //} + } // Is this an old style single-touch driver? } else if (test_bit(BTN_TOUCH, device->keyBitmask) && test_bit(ABS_X, device->absBitmask) @@ -1003,6 +1156,11 @@ status_t EventHub::openDeviceLocked(const char *devicePath) { } } + // Check whether this device supports the vibrator. + if (test_bit(FF_RUMBLE, device->ffBitmask)) { + device->classes |= INPUT_DEVICE_CLASS_VIBRATOR; + } + // Configure virtual keys. if ((device->classes & INPUT_DEVICE_CLASS_TOUCH)) { // Load the virtual keys for the touch screen, if any. @@ -1025,7 +1183,7 @@ status_t EventHub::openDeviceLocked(const char *devicePath) { if (device->classes & INPUT_DEVICE_CLASS_KEYBOARD) { // Register the keyboard as a built-in keyboard if it is eligible. if (!keyMapStatus - && mBuiltInKeyboardId == -1 + && mBuiltInKeyboardId == NO_BUILT_IN_KEYBOARD && isEligibleBuiltInKeyboard(device->identifier, device->configuration, &device->keyMap)) { mBuiltInKeyboardId = device->id; @@ -1052,6 +1210,12 @@ status_t EventHub::openDeviceLocked(const char *devicePath) { break; } } + + // Disable kernel key repeat since we handle it ourselves + unsigned int repeatRate[] = {0,0}; + if (ioctl(fd, EVIOCSREP, repeatRate)) { + ALOGW("Unable to disable kernel key repeat for %s: %s", devicePath, strerror(errno)); + } } // If the device isn't recognized as something we handle, don't monitor it. @@ -1078,20 +1242,62 @@ status_t EventHub::openDeviceLocked(const char *devicePath) { return -1; } + // Enable wake-lock behavior on kernels that support it. + // TODO: Only need this for devices that can really wake the system. + bool usingSuspendBlockIoctl = !ioctl(fd, EVIOCSSUSPENDBLOCK, 1); + + // Tell the kernel that we want to use the monotonic clock for reporting timestamps + // associated with input events. This is important because the input system + // uses the timestamps extensively and assumes they were recorded using the monotonic + // clock. + // + // In older kernel, before Linux 3.4, there was no way to tell the kernel which + // clock to use to input event timestamps. The standard kernel behavior was to + // record a real time timestamp, which isn't what we want. Android kernels therefore + // contained a patch to the evdev_event() function in drivers/input/evdev.c to + // replace the call to do_gettimeofday() with ktime_get_ts() to cause the monotonic + // clock to be used instead of the real time clock. + // + // As of Linux 3.4, there is a new EVIOCSCLOCKID ioctl to set the desired clock. + // Therefore, we no longer require the Android-specific kernel patch described above + // as long as we make sure to set select the monotonic clock. We do that here. + int clockId = CLOCK_MONOTONIC; + bool usingClockIoctl = !ioctl(fd, EVIOCSCLOCKID, &clockId); + ALOGI("New device: id=%d, fd=%d, path='%s', name='%s', classes=0x%x, " - "configuration='%s', keyLayout='%s', keyCharacterMap='%s', builtinKeyboard=%s", + "configuration='%s', keyLayout='%s', keyCharacterMap='%s', builtinKeyboard=%s, " + "usingSuspendBlockIoctl=%s, usingClockIoctl=%s", deviceId, fd, devicePath, device->identifier.name.string(), device->classes, device->configurationFile.string(), device->keyMap.keyLayoutFile.string(), device->keyMap.keyCharacterMapFile.string(), - toString(mBuiltInKeyboardId == deviceId)); + toString(mBuiltInKeyboardId == deviceId), + toString(usingSuspendBlockIoctl), toString(usingClockIoctl)); - mDevices.add(deviceId, device); + addDeviceLocked(device); + return 0; +} +void EventHub::createVirtualKeyboardLocked() { + InputDeviceIdentifier identifier; + identifier.name = "Virtual"; + identifier.uniqueId = ""; + setDescriptor(identifier); + + Device* device = new Device(-1, VIRTUAL_KEYBOARD_ID, String8(""), identifier); + device->classes = INPUT_DEVICE_CLASS_KEYBOARD + | INPUT_DEVICE_CLASS_ALPHAKEY + | INPUT_DEVICE_CLASS_DPAD + | INPUT_DEVICE_CLASS_VIRTUAL; + loadKeyMapLocked(device); + addDeviceLocked(device); +} + +void EventHub::addDeviceLocked(Device* device) { + mDevices.add(device->id, device); device->next = mOpeningDevices; mOpeningDevices = device; - return 0; } void EventHub::loadConfigurationLocked(Device* device) { @@ -1178,11 +1384,13 @@ void EventHub::closeDeviceLocked(Device* device) { if (device->id == mBuiltInKeyboardId) { ALOGW("built-in keyboard device %s (id=%d) is closing! the apps will not like this", device->path.string(), mBuiltInKeyboardId); - mBuiltInKeyboardId = -1; + mBuiltInKeyboardId = NO_BUILT_IN_KEYBOARD; } - if (epoll_ctl(mEpollFd, EPOLL_CTL_DEL, device->fd, NULL)) { - ALOGW("Could not remove device fd from epoll instance. errno=%d", errno); + if (!device->isVirtual()) { + if (epoll_ctl(mEpollFd, EPOLL_CTL_DEL, device->fd, NULL)) { + ALOGW("Could not remove device fd from epoll instance. errno=%d", errno); + } } mDevices.removeItem(device->id); @@ -1312,6 +1520,7 @@ void EventHub::dump(String8& dump) { } dump.appendFormat(INDENT3 "Classes: 0x%08x\n", device->classes); dump.appendFormat(INDENT3 "Path: %s\n", device->path.string()); + dump.appendFormat(INDENT3 "Descriptor: %s\n", device->identifier.descriptor.string()); dump.appendFormat(INDENT3 "Location: %s\n", device->identifier.location.string()); dump.appendFormat(INDENT3 "UniqueId: %s\n", device->identifier.uniqueId.string()); dump.appendFormat(INDENT3 "Identifier: bus=0x%04x, vendor=0x%04x, " @@ -1324,6 +1533,8 @@ void EventHub::dump(String8& dump) { device->keyMap.keyCharacterMapFile.string()); dump.appendFormat(INDENT3 "ConfigurationFile: %s\n", device->configurationFile.string()); + dump.appendFormat(INDENT3 "HaveKeyboardLayoutOverlay: %s\n", + toString(device->overlayKeyMap != NULL)); } } // release lock } diff --git a/widget/gonk/libui/EventHub.h b/widget/gonk/libui/EventHub.h index 7881855684d5..e4e658b212e2 100644 --- a/widget/gonk/libui/EventHub.h +++ b/widget/gonk/libui/EventHub.h @@ -18,18 +18,19 @@ #ifndef _RUNTIME_EVENT_HUB_H #define _RUNTIME_EVENT_HUB_H -#include "utils_Log.h" #include "Input.h" +#include "InputDevice.h" #include "Keyboard.h" #include "KeyLayoutMap.h" #include "KeyCharacterMap.h" #include "VirtualKeyMap.h" -#include "String8.h" +#include #include +#include "cutils_log.h" #include #include #include -#include "PropertyMap.h" +#include #include #include @@ -38,11 +39,32 @@ /* Convenience constants. */ -#define BTN_FIRST 0x100 // first button scancode -#define BTN_LAST 0x15f // last button scancode +#define BTN_FIRST 0x100 // first button code +#define BTN_LAST 0x15f // last button code + +/* + * These constants are used privately in Android to pass raw timestamps + * through evdev from uinput device drivers because there is currently no + * other way to transfer this information. The evdev driver automatically + * timestamps all input events with the time they were posted and clobbers + * whatever information was passed in. + * + * For the purposes of this hack, the timestamp is specified in the + * CLOCK_MONOTONIC timebase and is split into two EV_MSC events specifying + * seconds and microseconds. + */ +#define MSC_ANDROID_TIME_SEC 0x6 +#define MSC_ANDROID_TIME_USEC 0x7 namespace android { +enum { + // Device id of a special "virtual" keyboard that is always present. + VIRTUAL_KEYBOARD_ID = -1, + // Device id of the "built-in" keyboard if there is one. + BUILT_IN_KEYBOARD_ID = 0, +}; + /* * A raw event as retrieved from the EventHub. */ @@ -50,10 +72,8 @@ struct RawEvent { nsecs_t when; int32_t deviceId; int32_t type; - int32_t scanCode; - int32_t keyCode; + int32_t code; int32_t value; - uint32_t flags; }; /* Describes an absolute axis. */ @@ -107,6 +127,12 @@ enum { /* The input device is a joystick (implies gamepad, has joystick absolute axes). */ INPUT_DEVICE_CLASS_JOYSTICK = 0x00000100, + /* The input device has a vibrator (supports FF_RUMBLE). */ + INPUT_DEVICE_CLASS_VIBRATOR = 0x00000200, + + /* The input device is virtual (not a real device, not part of UI configuration). */ + INPUT_DEVICE_CLASS_VIRTUAL = 0x40000000, + /* The input device is external (not built-in). */ INPUT_DEVICE_CLASS_EXTERNAL = 0x80000000, }; @@ -151,7 +177,7 @@ public: virtual uint32_t getDeviceClasses(int32_t deviceId) const = 0; - virtual String8 getDeviceName(int32_t deviceId) const = 0; + virtual InputDeviceIdentifier getDeviceIdentifier(int32_t deviceId) const = 0; virtual void getConfiguration(int32_t deviceId, PropertyMap* outConfiguration) const = 0; @@ -162,10 +188,10 @@ public: virtual bool hasInputProperty(int32_t deviceId, int property) const = 0; - virtual status_t mapKey(int32_t deviceId, int scancode, + virtual status_t mapKey(int32_t deviceId, int32_t scanCode, int32_t usageCode, int32_t* outKeycode, uint32_t* outFlags) const = 0; - virtual status_t mapAxis(int32_t deviceId, int scancode, + virtual status_t mapAxis(int32_t deviceId, int32_t scanCode, AxisInfo* outAxisInfo) const = 0; // Sets devices that are excluded from opening. @@ -208,7 +234,12 @@ public: virtual void getVirtualKeyDefinitions(int32_t deviceId, Vector& outVirtualKeys) const = 0; - virtual String8 getKeyCharacterMapFile(int32_t deviceId) const = 0; + virtual sp getKeyCharacterMap(int32_t deviceId) const = 0; + virtual bool setKeyboardLayoutOverlay(int32_t deviceId, const sp& map) = 0; + + /* Control the vibrator. */ + virtual void vibrate(int32_t deviceId, nsecs_t duration) = 0; + virtual void cancelVibrate(int32_t deviceId) = 0; /* Requests the EventHub to reopen all input devices on the next call to getEvents(). */ virtual void requestReopenDevices() = 0; @@ -230,7 +261,7 @@ public: virtual uint32_t getDeviceClasses(int32_t deviceId) const; - virtual String8 getDeviceName(int32_t deviceId) const; + virtual InputDeviceIdentifier getDeviceIdentifier(int32_t deviceId) const; virtual void getConfiguration(int32_t deviceId, PropertyMap* outConfiguration) const; @@ -241,10 +272,10 @@ public: virtual bool hasInputProperty(int32_t deviceId, int property) const; - virtual status_t mapKey(int32_t deviceId, int scancode, + virtual status_t mapKey(int32_t deviceId, int32_t scanCode, int32_t usageCode, int32_t* outKeycode, uint32_t* outFlags) const; - virtual status_t mapAxis(int32_t deviceId, int scancode, + virtual status_t mapAxis(int32_t deviceId, int32_t scanCode, AxisInfo* outAxisInfo) const; virtual void setExcludedDevices(const Vector& devices); @@ -266,7 +297,11 @@ public: virtual void getVirtualKeyDefinitions(int32_t deviceId, Vector& outVirtualKeys) const; - virtual String8 getKeyCharacterMapFile(int32_t deviceId) const; + virtual sp getKeyCharacterMap(int32_t deviceId) const; + virtual bool setKeyboardLayoutOverlay(int32_t deviceId, const sp& map); + + virtual void vibrate(int32_t deviceId, nsecs_t duration); + virtual void cancelVibrate(int32_t deviceId); virtual void requestReopenDevices(); @@ -282,7 +317,7 @@ private: struct Device { Device* next; - int fd; + int fd; // may be -1 if device is virtual const int32_t id; const String8 path; const InputDeviceIdentifier identifier; @@ -294,6 +329,7 @@ private: uint8_t relBitmask[(REL_MAX + 1) / 8]; uint8_t swBitmask[(SW_MAX + 1) / 8]; uint8_t ledBitmask[(LED_MAX + 1) / 8]; + uint8_t ffBitmask[(FF_MAX + 1) / 8]; uint8_t propBitmask[(INPUT_PROP_MAX + 1) / 8]; String8 configurationFile; @@ -301,15 +337,35 @@ private: VirtualKeyMap* virtualKeyMap; KeyMap keyMap; + sp overlayKeyMap; + sp combinedKeyMap; + + bool ffEffectPlaying; + int16_t ffEffectId; // initially -1 + + int32_t timestampOverrideSec; + int32_t timestampOverrideUsec; + Device(int fd, int32_t id, const String8& path, const InputDeviceIdentifier& identifier); ~Device(); void close(); + + inline bool isVirtual() const { return fd < 0; } + + const sp& getKeyCharacterMap() const { + if (combinedKeyMap != NULL) { + return combinedKeyMap; + } + return keyMap.keyCharacterMap; + } }; status_t openDeviceLocked(const char *devicePath); - status_t closeDeviceByPathLocked(const char *devicePath); + void createVirtualKeyboardLocked(); + void addDeviceLocked(Device* device); + status_t closeDeviceByPathLocked(const char *devicePath); void closeDeviceLocked(Device* device); void closeAllDevicesLocked(); @@ -331,8 +387,13 @@ private: // Protect all internal state. mutable Mutex mLock; - // The actual id of the built-in keyboard, or -1 if none. + // The actual id of the built-in keyboard, or NO_BUILT_IN_KEYBOARD if none. // EventHub remaps the built-in keyboard to id 0 externally as required by the API. + enum { + // Must not conflict with any other assigned device ids, including + // the virtual keyboard id (-1). + NO_BUILT_IN_KEYBOARD = -2, + }; int32_t mBuiltInKeyboardId; int32_t mNextDeviceId; @@ -367,9 +428,6 @@ private: size_t mPendingEventCount; size_t mPendingEventIndex; bool mPendingINotify; - - // Set to the number of CPUs. - int32_t mNumCpus; }; }; // namespace android diff --git a/widget/gonk/libui/Input.cpp b/widget/gonk/libui/Input.cpp index 264836e432e8..2208191e65a4 100644 --- a/widget/gonk/libui/Input.cpp +++ b/widget/gonk/libui/Input.cpp @@ -1,35 +1,28 @@ -// -// Copyright 2010 The Android Open Source Project -// -// Provides a pipe-based transport for native events in the NDK. -// +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + #define LOG_TAG "Input" - //#define LOG_NDEBUG 0 - -// Log debug messages about keymap probing. -#define DEBUG_PROBE 0 - -// Log debug messages about velocity tracking. -#define DEBUG_VELOCITY 0 - -// Log debug messages about least squares fitting. -#define DEBUG_LEAST_SQUARES 0 - -// Log debug messages about acceleration. -#define DEBUG_ACCELERATION 0 - - -#include -#include -#include - -#include "utils_Log.h" -#include "Input.h" +#include "cutils_log.h" #include #include +#include "Input.h" + #ifdef HAVE_ANDROID_OS #include @@ -40,106 +33,6 @@ namespace android { -static const char* CONFIGURATION_FILE_DIR[] = { - "idc/", - "keylayout/", - "keychars/", -}; - -static const char* CONFIGURATION_FILE_EXTENSION[] = { - ".idc", - ".kl", - ".kcm", -}; - -static bool isValidNameChar(char ch) { - return isascii(ch) && (isdigit(ch) || isalpha(ch) || ch == '-' || ch == '_'); -} - -static void appendInputDeviceConfigurationFileRelativePath(String8& path, - const String8& name, InputDeviceConfigurationFileType type) { - path.append(CONFIGURATION_FILE_DIR[type]); - for (size_t i = 0; i < name.length(); i++) { - char ch = name[i]; - if (!isValidNameChar(ch)) { - ch = '_'; - } - path.append(&ch, 1); - } - path.append(CONFIGURATION_FILE_EXTENSION[type]); -} - -String8 getInputDeviceConfigurationFilePathByDeviceIdentifier( - const InputDeviceIdentifier& deviceIdentifier, - InputDeviceConfigurationFileType type) { - if (deviceIdentifier.vendor !=0 && deviceIdentifier.product != 0) { - if (deviceIdentifier.version != 0) { - // Try vendor product version. - String8 versionPath(getInputDeviceConfigurationFilePathByName( - String8::format("Vendor_%04x_Product_%04x_Version_%04x", - deviceIdentifier.vendor, deviceIdentifier.product, - deviceIdentifier.version), - type)); - if (!versionPath.isEmpty()) { - return versionPath; - } - } - - // Try vendor product. - String8 productPath(getInputDeviceConfigurationFilePathByName( - String8::format("Vendor_%04x_Product_%04x", - deviceIdentifier.vendor, deviceIdentifier.product), - type)); - if (!productPath.isEmpty()) { - return productPath; - } - } - - // Try device name. - return getInputDeviceConfigurationFilePathByName(deviceIdentifier.name, type); -} - -String8 getInputDeviceConfigurationFilePathByName( - const String8& name, InputDeviceConfigurationFileType type) { - // Search system repository. - String8 path; - path.setTo(getenv("ANDROID_ROOT")); - path.append("/usr/"); - appendInputDeviceConfigurationFileRelativePath(path, name, type); -#if DEBUG_PROBE - ALOGD("Probing for system provided input device configuration file: path='%s'", path.string()); -#endif - if (!access(path.string(), R_OK)) { -#if DEBUG_PROBE - ALOGD("Found"); -#endif - return path; - } - - // Search user repository. - // TODO Should only look here if not in safe mode. - path.setTo(getenv("ANDROID_DATA")); - path.append("/system/devices/"); - appendInputDeviceConfigurationFileRelativePath(path, name, type); -#if DEBUG_PROBE - ALOGD("Probing for system user input device configuration file: path='%s'", path.string()); -#endif - if (!access(path.string(), R_OK)) { -#if DEBUG_PROBE - ALOGD("Found"); -#endif - return path; - } - - // Not found. -#if DEBUG_PROBE - ALOGD("Probe failed to find input device configuration file: name='%s', type=%d", - name.string(), type); -#endif - return String8(); -} - - // --- InputEvent --- void InputEvent::initialize(int32_t deviceId, int32_t source) { @@ -180,6 +73,8 @@ bool KeyEvent::hasDefaultAction(int32_t keyCode) { case AKEYCODE_MEDIA_RECORD: case AKEYCODE_MEDIA_FAST_FORWARD: case AKEYCODE_MUTE: + case AKEYCODE_BRIGHTNESS_DOWN: + case AKEYCODE_BRIGHTNESS_UP: return true; } @@ -216,6 +111,8 @@ bool KeyEvent::isSystemKey(int32_t keyCode) { case AKEYCODE_CAMERA: case AKEYCODE_FOCUS: case AKEYCODE_SEARCH: + case AKEYCODE_BRIGHTNESS_DOWN: + case AKEYCODE_BRIGHTNESS_UP: return true; } @@ -329,7 +226,7 @@ status_t PointerCoords::readFromParcel(Parcel* parcel) { } for (uint32_t i = 0; i < count; i++) { - values[i] = parcel->readInt32(); + values[i] = parcel->readFloat(); } return OK; } @@ -339,7 +236,7 @@ status_t PointerCoords::writeToParcel(Parcel* parcel) const { uint32_t count = __builtin_popcountll(bits); for (uint32_t i = 0; i < count; i++) { - parcel->writeInt32(values[i]); + parcel->writeFloat(values[i]); } return OK; } @@ -684,541 +581,55 @@ bool MotionEvent::isTouchEvent(int32_t source, int32_t action) { } -// --- VelocityTracker --- +// --- PooledInputEventFactory --- -const uint32_t VelocityTracker::DEFAULT_DEGREE; -const nsecs_t VelocityTracker::DEFAULT_HORIZON; -const uint32_t VelocityTracker::HISTORY_SIZE; - -static inline float vectorDot(const float* a, const float* b, uint32_t m) { - float r = 0; - while (m--) { - r += *(a++) * *(b++); - } - return r; +PooledInputEventFactory::PooledInputEventFactory(size_t maxPoolSize) : + mMaxPoolSize(maxPoolSize) { } -static inline float vectorNorm(const float* a, uint32_t m) { - float r = 0; - while (m--) { - float t = *(a++); - r += t * t; +PooledInputEventFactory::~PooledInputEventFactory() { + for (size_t i = 0; i < mKeyEventPool.size(); i++) { + delete mKeyEventPool.itemAt(i); + } + for (size_t i = 0; i < mMotionEventPool.size(); i++) { + delete mMotionEventPool.itemAt(i); } - return sqrtf(r); } -#if DEBUG_LEAST_SQUARES || DEBUG_VELOCITY -static String8 vectorToString(const float* a, uint32_t m) { - String8 str; - str.append("["); - while (m--) { - str.appendFormat(" %f", *(a++)); - if (m) { - str.append(","); +KeyEvent* PooledInputEventFactory::createKeyEvent() { + if (!mKeyEventPool.isEmpty()) { + KeyEvent* event = mKeyEventPool.top(); + mKeyEventPool.pop(); + return event; + } + return new KeyEvent(); +} + +MotionEvent* PooledInputEventFactory::createMotionEvent() { + if (!mMotionEventPool.isEmpty()) { + MotionEvent* event = mMotionEventPool.top(); + mMotionEventPool.pop(); + return event; + } + return new MotionEvent(); +} + +void PooledInputEventFactory::recycle(InputEvent* event) { + switch (event->getType()) { + case AINPUT_EVENT_TYPE_KEY: + if (mKeyEventPool.size() < mMaxPoolSize) { + mKeyEventPool.push(static_cast(event)); + return; } - } - str.append(" ]"); - return str; -} - -static String8 matrixToString(const float* a, uint32_t m, uint32_t n, bool rowMajor) { - String8 str; - str.append("["); - for (size_t i = 0; i < m; i++) { - if (i) { - str.append(","); - } - str.append(" ["); - for (size_t j = 0; j < n; j++) { - if (j) { - str.append(","); - } - str.appendFormat(" %f", a[rowMajor ? i * n + j : j * m + i]); - } - str.append(" ]"); - } - str.append(" ]"); - return str; -} -#endif - -VelocityTracker::VelocityTracker() { - clear(); -} - -void VelocityTracker::clear() { - mIndex = 0; - mMovements[0].idBits.clear(); - mActivePointerId = -1; -} - -void VelocityTracker::clearPointers(BitSet32 idBits) { - BitSet32 remainingIdBits(mMovements[mIndex].idBits.value & ~idBits.value); - mMovements[mIndex].idBits = remainingIdBits; - - if (mActivePointerId >= 0 && idBits.hasBit(mActivePointerId)) { - mActivePointerId = !remainingIdBits.isEmpty() ? remainingIdBits.firstMarkedBit() : -1; - } -} - -void VelocityTracker::addMovement(nsecs_t eventTime, BitSet32 idBits, const Position* positions) { - if (++mIndex == HISTORY_SIZE) { - mIndex = 0; - } - - while (idBits.count() > MAX_POINTERS) { - idBits.clearLastMarkedBit(); - } - - Movement& movement = mMovements[mIndex]; - movement.eventTime = eventTime; - movement.idBits = idBits; - uint32_t count = idBits.count(); - for (uint32_t i = 0; i < count; i++) { - movement.positions[i] = positions[i]; - } - - if (mActivePointerId < 0 || !idBits.hasBit(mActivePointerId)) { - mActivePointerId = count != 0 ? idBits.firstMarkedBit() : -1; - } - -#if DEBUG_VELOCITY - ALOGD("VelocityTracker: addMovement eventTime=%lld, idBits=0x%08x, activePointerId=%d", - eventTime, idBits.value, mActivePointerId); - for (BitSet32 iterBits(idBits); !iterBits.isEmpty(); ) { - uint32_t id = iterBits.firstMarkedBit(); - uint32_t index = idBits.getIndexOfBit(id); - iterBits.clearBit(id); - Estimator estimator; - getEstimator(id, DEFAULT_DEGREE, DEFAULT_HORIZON, &estimator); - ALOGD(" %d: position (%0.3f, %0.3f), " - "estimator (degree=%d, xCoeff=%s, yCoeff=%s, confidence=%f)", - id, positions[index].x, positions[index].y, - int(estimator.degree), - vectorToString(estimator.xCoeff, estimator.degree).string(), - vectorToString(estimator.yCoeff, estimator.degree).string(), - estimator.confidence); - } -#endif -} - -void VelocityTracker::addMovement(const MotionEvent* event) { - int32_t actionMasked = event->getActionMasked(); - - switch (actionMasked) { - case AMOTION_EVENT_ACTION_DOWN: - case AMOTION_EVENT_ACTION_HOVER_ENTER: - // Clear all pointers on down before adding the new movement. - clear(); break; - case AMOTION_EVENT_ACTION_POINTER_DOWN: { - // Start a new movement trace for a pointer that just went down. - // We do this on down instead of on up because the client may want to query the - // final velocity for a pointer that just went up. - BitSet32 downIdBits; - downIdBits.markBit(event->getPointerId(event->getActionIndex())); - clearPointers(downIdBits); + case AINPUT_EVENT_TYPE_MOTION: + if (mMotionEventPool.size() < mMaxPoolSize) { + mMotionEventPool.push(static_cast(event)); + return; + } break; } - case AMOTION_EVENT_ACTION_MOVE: - case AMOTION_EVENT_ACTION_HOVER_MOVE: - break; - default: - // Ignore all other actions because they do not convey any new information about - // pointer movement. We also want to preserve the last known velocity of the pointers. - // Note that ACTION_UP and ACTION_POINTER_UP always report the last known position - // of the pointers that went up. ACTION_POINTER_UP does include the new position of - // pointers that remained down but we will also receive an ACTION_MOVE with this - // information if any of them actually moved. Since we don't know how many pointers - // will be going up at once it makes sense to just wait for the following ACTION_MOVE - // before adding the movement. - return; - } - - size_t pointerCount = event->getPointerCount(); - if (pointerCount > MAX_POINTERS) { - pointerCount = MAX_POINTERS; - } - - BitSet32 idBits; - for (size_t i = 0; i < pointerCount; i++) { - idBits.markBit(event->getPointerId(i)); - } - - nsecs_t eventTime; - Position positions[pointerCount]; - - size_t historySize = event->getHistorySize(); - for (size_t h = 0; h < historySize; h++) { - eventTime = event->getHistoricalEventTime(h); - for (size_t i = 0; i < pointerCount; i++) { - positions[i].x = event->getHistoricalX(i, h); - positions[i].y = event->getHistoricalY(i, h); - } - addMovement(eventTime, idBits, positions); - } - - eventTime = event->getEventTime(); - for (size_t i = 0; i < pointerCount; i++) { - positions[i].x = event->getX(i); - positions[i].y = event->getY(i); - } - addMovement(eventTime, idBits, positions); -} - -/** - * Solves a linear least squares problem to obtain a N degree polynomial that fits - * the specified input data as nearly as possible. - * - * Returns true if a solution is found, false otherwise. - * - * The input consists of two vectors of data points X and Y with indices 0..m-1. - * The output is a vector B with indices 0..n-1 that describes a polynomial - * that fits the data, such the sum of abs(Y[i] - (B[0] + B[1] X[i] + B[2] X[i]^2 ... B[n] X[i]^n)) - * for all i between 0 and m-1 is minimized. - * - * That is to say, the function that generated the input data can be approximated - * by y(x) ~= B[0] + B[1] x + B[2] x^2 + ... + B[n] x^n. - * - * The coefficient of determination (R^2) is also returned to describe the goodness - * of fit of the model for the given data. It is a value between 0 and 1, where 1 - * indicates perfect correspondence. - * - * This function first expands the X vector to a m by n matrix A such that - * A[i][0] = 1, A[i][1] = X[i], A[i][2] = X[i]^2, ..., A[i][n] = X[i]^n. - * - * Then it calculates the QR decomposition of A yielding an m by m orthonormal matrix Q - * and an m by n upper triangular matrix R. Because R is upper triangular (lower - * part is all zeroes), we can simplify the decomposition into an m by n matrix - * Q1 and a n by n matrix R1 such that A = Q1 R1. - * - * Finally we solve the system of linear equations given by R1 B = (Qtranspose Y) - * to find B. - * - * For efficiency, we lay out A and Q column-wise in memory because we frequently - * operate on the column vectors. Conversely, we lay out R row-wise. - * - * http://en.wikipedia.org/wiki/Numerical_methods_for_linear_least_squares - * http://en.wikipedia.org/wiki/Gram-Schmidt - */ -static bool solveLeastSquares(const float* x, const float* y, uint32_t m, uint32_t n, - float* outB, float* outDet) { -#if DEBUG_LEAST_SQUARES - ALOGD("solveLeastSquares: m=%d, n=%d, x=%s, y=%s", int(m), int(n), - vectorToString(x, m).string(), vectorToString(y, m).string()); -#endif - - // Expand the X vector to a matrix A. - float a[n][m]; // column-major order - for (uint32_t h = 0; h < m; h++) { - a[0][h] = 1; - for (uint32_t i = 1; i < n; i++) { - a[i][h] = a[i - 1][h] * x[h]; - } - } -#if DEBUG_LEAST_SQUARES - ALOGD(" - a=%s", matrixToString(&a[0][0], m, n, false /*rowMajor*/).string()); -#endif - - // Apply the Gram-Schmidt process to A to obtain its QR decomposition. - float q[n][m]; // orthonormal basis, column-major order - float r[n][n]; // upper triangular matrix, row-major order - for (uint32_t j = 0; j < n; j++) { - for (uint32_t h = 0; h < m; h++) { - q[j][h] = a[j][h]; - } - for (uint32_t i = 0; i < j; i++) { - float dot = vectorDot(&q[j][0], &q[i][0], m); - for (uint32_t h = 0; h < m; h++) { - q[j][h] -= dot * q[i][h]; - } - } - - float norm = vectorNorm(&q[j][0], m); - if (norm < 0.000001f) { - // vectors are linearly dependent or zero so no solution -#if DEBUG_LEAST_SQUARES - ALOGD(" - no solution, norm=%f", norm); -#endif - return false; - } - - float invNorm = 1.0f / norm; - for (uint32_t h = 0; h < m; h++) { - q[j][h] *= invNorm; - } - for (uint32_t i = 0; i < n; i++) { - r[j][i] = i < j ? 0 : vectorDot(&q[j][0], &a[i][0], m); - } - } -#if DEBUG_LEAST_SQUARES - ALOGD(" - q=%s", matrixToString(&q[0][0], m, n, false /*rowMajor*/).string()); - ALOGD(" - r=%s", matrixToString(&r[0][0], n, n, true /*rowMajor*/).string()); - - // calculate QR, if we factored A correctly then QR should equal A - float qr[n][m]; - for (uint32_t h = 0; h < m; h++) { - for (uint32_t i = 0; i < n; i++) { - qr[i][h] = 0; - for (uint32_t j = 0; j < n; j++) { - qr[i][h] += q[j][h] * r[j][i]; - } - } - } - ALOGD(" - qr=%s", matrixToString(&qr[0][0], m, n, false /*rowMajor*/).string()); -#endif - - // Solve R B = Qt Y to find B. This is easy because R is upper triangular. - // We just work from bottom-right to top-left calculating B's coefficients. - for (uint32_t i = n; i-- != 0; ) { - outB[i] = vectorDot(&q[i][0], y, m); - for (uint32_t j = n - 1; j > i; j--) { - outB[i] -= r[i][j] * outB[j]; - } - outB[i] /= r[i][i]; - } -#if DEBUG_LEAST_SQUARES - ALOGD(" - b=%s", vectorToString(outB, n).string()); -#endif - - // Calculate the coefficient of determination as 1 - (SSerr / SStot) where - // SSerr is the residual sum of squares (squared variance of the error), - // and SStot is the total sum of squares (squared variance of the data). - float ymean = 0; - for (uint32_t h = 0; h < m; h++) { - ymean += y[h]; - } - ymean /= m; - - float sserr = 0; - float sstot = 0; - for (uint32_t h = 0; h < m; h++) { - float err = y[h] - outB[0]; - float term = 1; - for (uint32_t i = 1; i < n; i++) { - term *= x[h]; - err -= term * outB[i]; - } - sserr += err * err; - float var = y[h] - ymean; - sstot += var * var; - } - *outDet = sstot > 0.000001f ? 1.0f - (sserr / sstot) : 1; -#if DEBUG_LEAST_SQUARES - ALOGD(" - sserr=%f", sserr); - ALOGD(" - sstot=%f", sstot); - ALOGD(" - det=%f", *outDet); -#endif - return true; -} - -bool VelocityTracker::getVelocity(uint32_t id, float* outVx, float* outVy) const { - Estimator estimator; - if (getEstimator(id, DEFAULT_DEGREE, DEFAULT_HORIZON, &estimator)) { - if (estimator.degree >= 1) { - *outVx = estimator.xCoeff[1]; - *outVy = estimator.yCoeff[1]; - return true; - } - } - *outVx = 0; - *outVy = 0; - return false; -} - -bool VelocityTracker::getEstimator(uint32_t id, uint32_t degree, nsecs_t horizon, - Estimator* outEstimator) const { - outEstimator->clear(); - - // Iterate over movement samples in reverse time order and collect samples. - float x[HISTORY_SIZE]; - float y[HISTORY_SIZE]; - float time[HISTORY_SIZE]; - uint32_t m = 0; - uint32_t index = mIndex; - const Movement& newestMovement = mMovements[mIndex]; - do { - const Movement& movement = mMovements[index]; - if (!movement.idBits.hasBit(id)) { - break; - } - - nsecs_t age = newestMovement.eventTime - movement.eventTime; - if (age > horizon) { - break; - } - - const Position& position = movement.getPosition(id); - x[m] = position.x; - y[m] = position.y; - time[m] = -age * 0.000000001f; - index = (index == 0 ? HISTORY_SIZE : index) - 1; - } while (++m < HISTORY_SIZE); - - if (m == 0) { - return false; // no data - } - - // Calculate a least squares polynomial fit. - if (degree > Estimator::MAX_DEGREE) { - degree = Estimator::MAX_DEGREE; - } - if (degree > m - 1) { - degree = m - 1; - } - if (degree >= 1) { - float xdet, ydet; - uint32_t n = degree + 1; - if (solveLeastSquares(time, x, m, n, outEstimator->xCoeff, &xdet) - && solveLeastSquares(time, y, m, n, outEstimator->yCoeff, &ydet)) { - outEstimator->degree = degree; - outEstimator->confidence = xdet * ydet; -#if DEBUG_LEAST_SQUARES - ALOGD("estimate: degree=%d, xCoeff=%s, yCoeff=%s, confidence=%f", - int(outEstimator->degree), - vectorToString(outEstimator->xCoeff, n).string(), - vectorToString(outEstimator->yCoeff, n).string(), - outEstimator->confidence); -#endif - return true; - } - } - - // No velocity data available for this pointer, but we do have its current position. - outEstimator->xCoeff[0] = x[0]; - outEstimator->yCoeff[0] = y[0]; - outEstimator->degree = 0; - outEstimator->confidence = 1; - return true; -} - - -// --- VelocityControl --- - -const nsecs_t VelocityControl::STOP_TIME; - -VelocityControl::VelocityControl() { - reset(); -} - -void VelocityControl::setParameters(const VelocityControlParameters& parameters) { - mParameters = parameters; - reset(); -} - -void VelocityControl::reset() { - mLastMovementTime = LLONG_MIN; - mRawPosition.x = 0; - mRawPosition.y = 0; - mVelocityTracker.clear(); -} - -void VelocityControl::move(nsecs_t eventTime, float* deltaX, float* deltaY) { - if ((deltaX && *deltaX) || (deltaY && *deltaY)) { - if (eventTime >= mLastMovementTime + STOP_TIME) { -#if DEBUG_ACCELERATION - ALOGD("VelocityControl: stopped, last movement was %0.3fms ago", - (eventTime - mLastMovementTime) * 0.000001f); -#endif - reset(); - } - - mLastMovementTime = eventTime; - if (deltaX) { - mRawPosition.x += *deltaX; - } - if (deltaY) { - mRawPosition.y += *deltaY; - } - mVelocityTracker.addMovement(eventTime, BitSet32(BitSet32::valueForBit(0)), &mRawPosition); - - float vx, vy; - float scale = mParameters.scale; - if (mVelocityTracker.getVelocity(0, &vx, &vy)) { - float speed = hypotf(vx, vy) * scale; - if (speed >= mParameters.highThreshold) { - // Apply full acceleration above the high speed threshold. - scale *= mParameters.acceleration; - } else if (speed > mParameters.lowThreshold) { - // Linearly interpolate the acceleration to apply between the low and high - // speed thresholds. - scale *= 1 + (speed - mParameters.lowThreshold) - / (mParameters.highThreshold - mParameters.lowThreshold) - * (mParameters.acceleration - 1); - } - -#if DEBUG_ACCELERATION - ALOGD("VelocityControl(%0.3f, %0.3f, %0.3f, %0.3f): " - "vx=%0.3f, vy=%0.3f, speed=%0.3f, accel=%0.3f", - mParameters.scale, mParameters.lowThreshold, mParameters.highThreshold, - mParameters.acceleration, - vx, vy, speed, scale / mParameters.scale); -#endif - } else { -#if DEBUG_ACCELERATION - ALOGD("VelocityControl(%0.3f, %0.3f, %0.3f, %0.3f): unknown velocity", - mParameters.scale, mParameters.lowThreshold, mParameters.highThreshold, - mParameters.acceleration); -#endif - } - - if (deltaX) { - *deltaX *= scale; - } - if (deltaY) { - *deltaY *= scale; - } - } -} - - -// --- InputDeviceInfo --- - -InputDeviceInfo::InputDeviceInfo() { - initialize(-1, String8("uninitialized device info")); -} - -InputDeviceInfo::InputDeviceInfo(const InputDeviceInfo& other) : - mId(other.mId), mName(other.mName), mSources(other.mSources), - mKeyboardType(other.mKeyboardType), - mMotionRanges(other.mMotionRanges) { -} - -InputDeviceInfo::~InputDeviceInfo() { -} - -void InputDeviceInfo::initialize(int32_t id, const String8& name) { - mId = id; - mName = name; - mSources = 0; - mKeyboardType = AINPUT_KEYBOARD_TYPE_NONE; - mMotionRanges.clear(); -} - -const InputDeviceInfo::MotionRange* InputDeviceInfo::getMotionRange( - int32_t axis, uint32_t source) const { - size_t numRanges = mMotionRanges.size(); - for (size_t i = 0; i < numRanges; i++) { - const MotionRange& range = mMotionRanges.itemAt(i); - if (range.axis == axis && range.source == source) { - return ⦥ - } - } - return NULL; -} - -void InputDeviceInfo::addSource(uint32_t source) { - mSources |= source; -} - -void InputDeviceInfo::addMotionRange(int32_t axis, uint32_t source, float min, float max, - float flat, float fuzz) { - MotionRange range = { axis, source, min, max, flat, fuzz }; - mMotionRanges.add(range); -} - -void InputDeviceInfo::addMotionRange(const MotionRange& range) { - mMotionRanges.add(range); + delete event; } } // namespace android diff --git a/widget/gonk/libui/Input.h b/widget/gonk/libui/Input.h index 6c17518369cd..3d958bfabbe3 100644 --- a/widget/gonk/libui/Input.h +++ b/widget/gonk/libui/Input.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef _UI_INPUT_H -#define _UI_INPUT_H +#ifndef _ANDROIDFW_INPUT_H +#define _ANDROIDFW_INPUT_H /** * Native input event structures. @@ -24,10 +24,9 @@ #include "android_input.h" #include #include -#include "Timers.h" +#include #include -#include "String8.h" -#include "BitSet.h" +#include #ifdef HAVE_ANDROID_OS class SkMatrix; @@ -37,6 +36,9 @@ class SkMatrix; * Additional private constants not defined in ndk/ui/input.h. */ enum { + /* Signifies that the key is being predispatched */ + AKEY_EVENT_FLAG_PREDISPATCH = 0x20000000, + /* Private control to determine when an app is tracking a key sequence. */ AKEY_EVENT_FLAG_START_TRACKING = 0x40000000, @@ -49,6 +51,15 @@ enum { AMOTION_EVENT_FLAG_TAINTED = 0x80000000, }; +enum { + /* Used when a motion event is not associated with any display. + * Typically used for non-pointer events. */ + ADISPLAY_ID_NONE = -1, + + /* The default display id. */ + ADISPLAY_ID_DEFAULT = 0, +}; + enum { /* * Indicates that an input device has switches. @@ -156,37 +167,6 @@ enum { POLICY_FLAG_PASS_TO_USER = 0x40000000, }; -/* - * Describes the basic configuration of input devices that are present. - */ -struct InputConfiguration { - enum { - TOUCHSCREEN_UNDEFINED = 0, - TOUCHSCREEN_NOTOUCH = 1, - TOUCHSCREEN_STYLUS = 2, - TOUCHSCREEN_FINGER = 3 - }; - - enum { - KEYBOARD_UNDEFINED = 0, - KEYBOARD_NOKEYS = 1, - KEYBOARD_QWERTY = 2, - KEYBOARD_12KEY = 3 - }; - - enum { - NAVIGATION_UNDEFINED = 0, - NAVIGATION_NONAV = 1, - NAVIGATION_DPAD = 2, - NAVIGATION_TRACKBALL = 3, - NAVIGATION_WHEEL = 4 - }; - - int32_t touchScreen; - int32_t keyboard; - int32_t navigation; -}; - /* * Pointer coordinate data. */ @@ -292,6 +272,8 @@ public: inline int32_t getFlags() const { return mFlags; } + inline void setFlags(int32_t flags) { mFlags = flags; } + inline int32_t getKeyCode() const { return mKeyCode; } inline int32_t getScanCode() const { return mScanCode; } @@ -616,282 +598,25 @@ private: }; /* - * Calculates the velocity of pointer movements over time. + * An input event factory implementation that maintains a pool of input events. */ -class VelocityTracker { +class PooledInputEventFactory : public InputEventFactoryInterface { public: - // Default polynomial degree. (used by getVelocity) - static const uint32_t DEFAULT_DEGREE = 2; + PooledInputEventFactory(size_t maxPoolSize = 20); + virtual ~PooledInputEventFactory(); - // Default sample horizon. (used by getVelocity) - // We don't use too much history by default since we want to react to quick - // changes in direction. - static const nsecs_t DEFAULT_HORIZON = 100 * 1000000; // 100 ms + virtual KeyEvent* createKeyEvent(); + virtual MotionEvent* createMotionEvent(); - struct Position { - float x, y; - }; - - struct Estimator { - static const size_t MAX_DEGREE = 2; - - // Polynomial coefficients describing motion in X and Y. - float xCoeff[MAX_DEGREE + 1], yCoeff[MAX_DEGREE + 1]; - - // Polynomial degree (number of coefficients), or zero if no information is - // available. - uint32_t degree; - - // Confidence (coefficient of determination), between 0 (no fit) and 1 (perfect fit). - float confidence; - - inline void clear() { - degree = 0; - confidence = 0; - for (size_t i = 0; i <= MAX_DEGREE; i++) { - xCoeff[i] = 0; - yCoeff[i] = 0; - } - } - }; - - VelocityTracker(); - - // Resets the velocity tracker state. - void clear(); - - // Resets the velocity tracker state for specific pointers. - // Call this method when some pointers have changed and may be reusing - // an id that was assigned to a different pointer earlier. - void clearPointers(BitSet32 idBits); - - // Adds movement information for a set of pointers. - // The idBits bitfield specifies the pointer ids of the pointers whose positions - // are included in the movement. - // The positions array contains position information for each pointer in order by - // increasing id. Its size should be equal to the number of one bits in idBits. - void addMovement(nsecs_t eventTime, BitSet32 idBits, const Position* positions); - - // Adds movement information for all pointers in a MotionEvent, including historical samples. - void addMovement(const MotionEvent* event); - - // Gets the velocity of the specified pointer id in position units per second. - // Returns false and sets the velocity components to zero if there is - // insufficient movement information for the pointer. - bool getVelocity(uint32_t id, float* outVx, float* outVy) const; - - // Gets a quadratic estimator for the movements of the specified pointer id. - // Returns false and clears the estimator if there is no information available - // about the pointer. - bool getEstimator(uint32_t id, uint32_t degree, nsecs_t horizon, - Estimator* outEstimator) const; - - // Gets the active pointer id, or -1 if none. - inline int32_t getActivePointerId() const { return mActivePointerId; } - - // Gets a bitset containing all pointer ids from the most recent movement. - inline BitSet32 getCurrentPointerIdBits() const { return mMovements[mIndex].idBits; } + void recycle(InputEvent* event); private: - // Number of samples to keep. - static const uint32_t HISTORY_SIZE = 20; + const size_t mMaxPoolSize; - struct Movement { - nsecs_t eventTime; - BitSet32 idBits; - Position positions[MAX_POINTERS]; - - inline const Position& getPosition(uint32_t id) const { - return positions[idBits.getIndexOfBit(id)]; - } - }; - - uint32_t mIndex; - Movement mMovements[HISTORY_SIZE]; - int32_t mActivePointerId; + Vector mKeyEventPool; + Vector mMotionEventPool; }; - -/* - * Specifies parameters that govern pointer or wheel acceleration. - */ -struct VelocityControlParameters { - // A scale factor that is multiplied with the raw velocity deltas - // prior to applying any other velocity control factors. The scale - // factor should be used to adapt the input device resolution - // (eg. counts per inch) to the output device resolution (eg. pixels per inch). - // - // Must be a positive value. - // Default is 1.0 (no scaling). - float scale; - - // The scaled speed at which acceleration begins to be applied. - // This value establishes the upper bound of a low speed regime for - // small precise motions that are performed without any acceleration. - // - // Must be a non-negative value. - // Default is 0.0 (no low threshold). - float lowThreshold; - - // The scaled speed at which maximum acceleration is applied. - // The difference between highThreshold and lowThreshold controls - // the range of speeds over which the acceleration factor is interpolated. - // The wider the range, the smoother the acceleration. - // - // Must be a non-negative value greater than or equal to lowThreshold. - // Default is 0.0 (no high threshold). - float highThreshold; - - // The acceleration factor. - // When the speed is above the low speed threshold, the velocity will scaled - // by an interpolated value between 1.0 and this amount. - // - // Must be a positive greater than or equal to 1.0. - // Default is 1.0 (no acceleration). - float acceleration; - - VelocityControlParameters() : - scale(1.0f), lowThreshold(0.0f), highThreshold(0.0f), acceleration(1.0f) { - } - - VelocityControlParameters(float scale, float lowThreshold, - float highThreshold, float acceleration) : - scale(scale), lowThreshold(lowThreshold), - highThreshold(highThreshold), acceleration(acceleration) { - } -}; - -/* - * Implements mouse pointer and wheel speed control and acceleration. - */ -class VelocityControl { -public: - VelocityControl(); - - /* Sets the various parameters. */ - void setParameters(const VelocityControlParameters& parameters); - - /* Resets the current movement counters to zero. - * This has the effect of nullifying any acceleration. */ - void reset(); - - /* Translates a raw movement delta into an appropriately - * scaled / accelerated delta based on the current velocity. */ - void move(nsecs_t eventTime, float* deltaX, float* deltaY); - -private: - // If no movements are received within this amount of time, - // we assume the movement has stopped and reset the movement counters. - static const nsecs_t STOP_TIME = 500 * 1000000; // 500 ms - - VelocityControlParameters mParameters; - - nsecs_t mLastMovementTime; - VelocityTracker::Position mRawPosition; - VelocityTracker mVelocityTracker; -}; - - -/* - * Describes the characteristics and capabilities of an input device. - */ -class InputDeviceInfo { -public: - InputDeviceInfo(); - InputDeviceInfo(const InputDeviceInfo& other); - ~InputDeviceInfo(); - - struct MotionRange { - int32_t axis; - uint32_t source; - float min; - float max; - float flat; - float fuzz; - }; - - void initialize(int32_t id, const String8& name); - - inline int32_t getId() const { return mId; } - inline const String8 getName() const { return mName; } - inline uint32_t getSources() const { return mSources; } - - const MotionRange* getMotionRange(int32_t axis, uint32_t source) const; - - void addSource(uint32_t source); - void addMotionRange(int32_t axis, uint32_t source, - float min, float max, float flat, float fuzz); - void addMotionRange(const MotionRange& range); - - inline void setKeyboardType(int32_t keyboardType) { mKeyboardType = keyboardType; } - inline int32_t getKeyboardType() const { return mKeyboardType; } - - inline void setKeyCharacterMapFile(const String8& value) { mKeyCharacterMapFile = value; } - inline const String8& getKeyCharacterMapFile() const { return mKeyCharacterMapFile; } - - inline const Vector& getMotionRanges() const { - return mMotionRanges; - } - -private: - int32_t mId; - String8 mName; - uint32_t mSources; - int32_t mKeyboardType; - String8 mKeyCharacterMapFile; - - Vector mMotionRanges; -}; - -/* - * Identifies a device. - */ -struct InputDeviceIdentifier { - inline InputDeviceIdentifier() : - bus(0), vendor(0), product(0), version(0) { - } - - String8 name; - String8 location; - String8 uniqueId; - uint16_t bus; - uint16_t vendor; - uint16_t product; - uint16_t version; -}; - -/* Types of input device configuration files. */ -enum InputDeviceConfigurationFileType { - INPUT_DEVICE_CONFIGURATION_FILE_TYPE_CONFIGURATION = 0, /* .idc file */ - INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_LAYOUT = 1, /* .kl file */ - INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_CHARACTER_MAP = 2, /* .kcm file */ -}; - -/* - * Gets the path of an input device configuration file, if one is available. - * Considers both system provided and user installed configuration files. - * - * The device identifier is used to construct several default configuration file - * names to try based on the device name, vendor, product, and version. - * - * Returns an empty string if not found. - */ -extern String8 getInputDeviceConfigurationFilePathByDeviceIdentifier( - const InputDeviceIdentifier& deviceIdentifier, - InputDeviceConfigurationFileType type); - -/* - * Gets the path of an input device configuration file, if one is available. - * Considers both system provided and user installed configuration files. - * - * The name is case-sensitive and is used to construct the filename to resolve. - * All characters except 'a'-'z', 'A'-'Z', '0'-'9', '-', and '_' are replaced by underscores. - * - * Returns an empty string if not found. - */ -extern String8 getInputDeviceConfigurationFilePathByName( - const String8& name, InputDeviceConfigurationFileType type); - } // namespace android -#endif // _UI_INPUT_H +#endif // _ANDROIDFW_INPUT_H diff --git a/widget/gonk/libui/InputApplication.h b/widget/gonk/libui/InputApplication.h index ae9381b0cc59..ba789559c976 100644 --- a/widget/gonk/libui/InputApplication.h +++ b/widget/gonk/libui/InputApplication.h @@ -20,8 +20,8 @@ #include "Input.h" #include -#include "Timers.h" -#include "String8.h" +#include +#include namespace android { diff --git a/widget/gonk/libui/InputDevice.cpp b/widget/gonk/libui/InputDevice.cpp new file mode 100644 index 000000000000..01a437dd4af2 --- /dev/null +++ b/widget/gonk/libui/InputDevice.cpp @@ -0,0 +1,184 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "InputDevice" + +#include +#include +#include + +#include "InputDevice.h" + +namespace android { + +static const char* CONFIGURATION_FILE_DIR[] = { + "idc/", + "keylayout/", + "keychars/", +}; + +static const char* CONFIGURATION_FILE_EXTENSION[] = { + ".idc", + ".kl", + ".kcm", +}; + +static bool isValidNameChar(char ch) { + return isascii(ch) && (isdigit(ch) || isalpha(ch) || ch == '-' || ch == '_'); +} + +static void appendInputDeviceConfigurationFileRelativePath(String8& path, + const String8& name, InputDeviceConfigurationFileType type) { + path.append(CONFIGURATION_FILE_DIR[type]); + for (size_t i = 0; i < name.length(); i++) { + char ch = name[i]; + if (!isValidNameChar(ch)) { + ch = '_'; + } + path.append(&ch, 1); + } + path.append(CONFIGURATION_FILE_EXTENSION[type]); +} + +String8 getInputDeviceConfigurationFilePathByDeviceIdentifier( + const InputDeviceIdentifier& deviceIdentifier, + InputDeviceConfigurationFileType type) { + if (deviceIdentifier.vendor !=0 && deviceIdentifier.product != 0) { + if (deviceIdentifier.version != 0) { + // Try vendor product version. + String8 versionPath(getInputDeviceConfigurationFilePathByName( + String8::format("Vendor_%04x_Product_%04x_Version_%04x", + deviceIdentifier.vendor, deviceIdentifier.product, + deviceIdentifier.version), + type)); + if (!versionPath.isEmpty()) { + return versionPath; + } + } + + // Try vendor product. + String8 productPath(getInputDeviceConfigurationFilePathByName( + String8::format("Vendor_%04x_Product_%04x", + deviceIdentifier.vendor, deviceIdentifier.product), + type)); + if (!productPath.isEmpty()) { + return productPath; + } + } + + // Try device name. + return getInputDeviceConfigurationFilePathByName(deviceIdentifier.name, type); +} + +String8 getInputDeviceConfigurationFilePathByName( + const String8& name, InputDeviceConfigurationFileType type) { + // Search system repository. + String8 path; + path.setTo(getenv("ANDROID_ROOT")); + path.append("/usr/"); + appendInputDeviceConfigurationFileRelativePath(path, name, type); +#if DEBUG_PROBE + ALOGD("Probing for system provided input device configuration file: path='%s'", path.string()); +#endif + if (!access(path.string(), R_OK)) { +#if DEBUG_PROBE + ALOGD("Found"); +#endif + return path; + } + + // Search user repository. + // TODO Should only look here if not in safe mode. + path.setTo(getenv("ANDROID_DATA")); + path.append("/system/devices/"); + appendInputDeviceConfigurationFileRelativePath(path, name, type); +#if DEBUG_PROBE + ALOGD("Probing for system user input device configuration file: path='%s'", path.string()); +#endif + if (!access(path.string(), R_OK)) { +#if DEBUG_PROBE + ALOGD("Found"); +#endif + return path; + } + + // Not found. +#if DEBUG_PROBE + ALOGD("Probe failed to find input device configuration file: name='%s', type=%d", + name.string(), type); +#endif + return String8(); +} + + +// --- InputDeviceInfo --- + +InputDeviceInfo::InputDeviceInfo() { + initialize(-1, -1, InputDeviceIdentifier(), String8(), false); +} + +InputDeviceInfo::InputDeviceInfo(const InputDeviceInfo& other) : + mId(other.mId), mGeneration(other.mGeneration), mIdentifier(other.mIdentifier), + mAlias(other.mAlias), mIsExternal(other.mIsExternal), mSources(other.mSources), + mKeyboardType(other.mKeyboardType), + mKeyCharacterMap(other.mKeyCharacterMap), + mHasVibrator(other.mHasVibrator), + mMotionRanges(other.mMotionRanges) { +} + +InputDeviceInfo::~InputDeviceInfo() { +} + +void InputDeviceInfo::initialize(int32_t id, int32_t generation, + const InputDeviceIdentifier& identifier, const String8& alias, bool isExternal) { + mId = id; + mGeneration = generation; + mIdentifier = identifier; + mAlias = alias; + mIsExternal = isExternal; + mSources = 0; + mKeyboardType = AINPUT_KEYBOARD_TYPE_NONE; + mHasVibrator = false; + mMotionRanges.clear(); +} + +const InputDeviceInfo::MotionRange* InputDeviceInfo::getMotionRange( + int32_t axis, uint32_t source) const { + size_t numRanges = mMotionRanges.size(); + for (size_t i = 0; i < numRanges; i++) { + const MotionRange& range = mMotionRanges.itemAt(i); + if (range.axis == axis && range.source == source) { + return ⦥ + } + } + return NULL; +} + +void InputDeviceInfo::addSource(uint32_t source) { + mSources |= source; +} + +void InputDeviceInfo::addMotionRange(int32_t axis, uint32_t source, float min, float max, + float flat, float fuzz, float resolution) { + MotionRange range = { axis, source, min, max, flat, fuzz, resolution }; + mMotionRanges.add(range); +} + +void InputDeviceInfo::addMotionRange(const MotionRange& range) { + mMotionRanges.add(range); +} + +} // namespace android diff --git a/widget/gonk/libui/InputDevice.h b/widget/gonk/libui/InputDevice.h new file mode 100644 index 000000000000..0ab5863c933f --- /dev/null +++ b/widget/gonk/libui/InputDevice.h @@ -0,0 +1,156 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _ANDROIDFW_INPUT_DEVICE_H +#define _ANDROIDFW_INPUT_DEVICE_H + +#include "Input.h" +#include "KeyCharacterMap.h" + +namespace android { + +/* + * Identifies a device. + */ +struct InputDeviceIdentifier { + inline InputDeviceIdentifier() : + bus(0), vendor(0), product(0), version(0) { + } + + // Information provided by the kernel. + String8 name; + String8 location; + String8 uniqueId; + uint16_t bus; + uint16_t vendor; + uint16_t product; + uint16_t version; + + // A composite input device descriptor string that uniquely identifies the device + // even across reboots or reconnections. The value of this field is used by + // upper layers of the input system to associate settings with individual devices. + // It is hashed from whatever kernel provided information is available. + // Ideally, the way this value is computed should not change between Android releases + // because that would invalidate persistent settings that rely on it. + String8 descriptor; +}; + +/* + * Describes the characteristics and capabilities of an input device. + */ +class InputDeviceInfo { +public: + InputDeviceInfo(); + InputDeviceInfo(const InputDeviceInfo& other); + ~InputDeviceInfo(); + + struct MotionRange { + int32_t axis; + uint32_t source; + float min; + float max; + float flat; + float fuzz; + float resolution; + }; + + void initialize(int32_t id, int32_t generation, const InputDeviceIdentifier& identifier, + const String8& alias, bool isExternal); + + inline int32_t getId() const { return mId; } + inline int32_t getGeneration() const { return mGeneration; } + inline const InputDeviceIdentifier& getIdentifier() const { return mIdentifier; } + inline const String8& getAlias() const { return mAlias; } + inline const String8& getDisplayName() const { + return mAlias.isEmpty() ? mIdentifier.name : mAlias; + } + inline bool isExternal() const { return mIsExternal; } + inline uint32_t getSources() const { return mSources; } + + const MotionRange* getMotionRange(int32_t axis, uint32_t source) const; + + void addSource(uint32_t source); + void addMotionRange(int32_t axis, uint32_t source, + float min, float max, float flat, float fuzz, float resolution); + void addMotionRange(const MotionRange& range); + + inline void setKeyboardType(int32_t keyboardType) { mKeyboardType = keyboardType; } + inline int32_t getKeyboardType() const { return mKeyboardType; } + + inline void setKeyCharacterMap(const sp& value) { + mKeyCharacterMap = value; + } + + inline sp getKeyCharacterMap() const { + return mKeyCharacterMap; + } + + inline void setVibrator(bool hasVibrator) { mHasVibrator = hasVibrator; } + inline bool hasVibrator() const { return mHasVibrator; } + + inline const Vector& getMotionRanges() const { + return mMotionRanges; + } + +private: + int32_t mId; + int32_t mGeneration; + InputDeviceIdentifier mIdentifier; + String8 mAlias; + bool mIsExternal; + uint32_t mSources; + int32_t mKeyboardType; + sp mKeyCharacterMap; + bool mHasVibrator; + + Vector mMotionRanges; +}; + +/* Types of input device configuration files. */ +enum InputDeviceConfigurationFileType { + INPUT_DEVICE_CONFIGURATION_FILE_TYPE_CONFIGURATION = 0, /* .idc file */ + INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_LAYOUT = 1, /* .kl file */ + INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_CHARACTER_MAP = 2, /* .kcm file */ +}; + +/* + * Gets the path of an input device configuration file, if one is available. + * Considers both system provided and user installed configuration files. + * + * The device identifier is used to construct several default configuration file + * names to try based on the device name, vendor, product, and version. + * + * Returns an empty string if not found. + */ +extern String8 getInputDeviceConfigurationFilePathByDeviceIdentifier( + const InputDeviceIdentifier& deviceIdentifier, + InputDeviceConfigurationFileType type); + +/* + * Gets the path of an input device configuration file, if one is available. + * Considers both system provided and user installed configuration files. + * + * The name is case-sensitive and is used to construct the filename to resolve. + * All characters except 'a'-'z', 'A'-'Z', '0'-'9', '-', and '_' are replaced by underscores. + * + * Returns an empty string if not found. + */ +extern String8 getInputDeviceConfigurationFilePathByName( + const String8& name, InputDeviceConfigurationFileType type); + +} // namespace android + +#endif // _ANDROIDFW_INPUT_DEVICE_H diff --git a/widget/gonk/libui/InputDispatcher.cpp b/widget/gonk/libui/InputDispatcher.cpp index 4d2770276fc9..09d47c73765b 100644 --- a/widget/gonk/libui/InputDispatcher.cpp +++ b/widget/gonk/libui/InputDispatcher.cpp @@ -15,8 +15,10 @@ */ #define LOG_TAG "InputDispatcher" +#define ATRACE_TAG ATRACE_TAG_INPUT //#define LOG_NDEBUG 0 +#include "cutils_log.h" // Log detailed debug messages about each inbound event notification to the dispatcher. #define DEBUG_INBOUND_EVENT_DETAILS 0 @@ -24,24 +26,15 @@ // Log detailed debug messages about each outbound event processed by the dispatcher. #define DEBUG_OUTBOUND_EVENT_DETAILS 0 -// Log debug messages about batching. -#define DEBUG_BATCHING 0 - // Log debug messages about the dispatch cycle. #define DEBUG_DISPATCH_CYCLE 0 // Log debug messages about registrations. #define DEBUG_REGISTRATION 0 -// Log debug messages about performance statistics. -#define DEBUG_PERFORMANCE_STATISTICS 0 - // Log debug messages about input event injection. #define DEBUG_INJECTION 0 -// Log debug messages about input event throttling. -#define DEBUG_THROTTLING 0 - // Log debug messages about input focus tracking. #define DEBUG_FOCUS 0 @@ -53,15 +46,19 @@ #include "InputDispatcher.h" +#include "Trace.h" #include "PowerManager.h" #include #include #include #include +#include #define INDENT " " #define INDENT2 " " +#define INDENT3 " " +#define INDENT4 " " namespace android { @@ -78,21 +75,14 @@ const nsecs_t APP_SWITCH_TIMEOUT = 500 * 1000000LL; // 0.5sec // before considering it stale and dropping it. const nsecs_t STALE_EVENT_TIMEOUT = 10000 * 1000000LL; // 10sec -// Motion samples that are received within this amount of time are simply coalesced -// when batched instead of being appended. This is done because some drivers update -// the location of pointers one at a time instead of all at once. -// For example, when there are 10 fingers down, the input dispatcher may receive 10 -// samples in quick succession with only one finger's location changed in each sample. -// -// This value effectively imposes an upper bound on the touch sampling rate. -// Touch sensors typically have a 50Hz - 200Hz sampling rate, so we expect distinct -// samples to become available 5-20ms apart but individual finger reports can trickle -// in over a period of 2-4ms or so. -// -// Empirical testing shows that a 2ms coalescing interval (500Hz) is not enough, -// a 3ms coalescing interval (333Hz) works well most of the time and doesn't introduce -// significant quantization noise on current hardware. -const nsecs_t MOTION_SAMPLE_COALESCE_INTERVAL = 3 * 1000000LL; // 3ms, 333Hz +// Amount of time to allow touch events to be streamed out to a connection before requiring +// that the first event be finished. This value extends the ANR timeout by the specified +// amount. For example, if streaming is allowed to get ahead by one second relative to the +// queue of waiting unfinished events, then ANRs will similarly be delayed by one second. +const nsecs_t STREAM_AHEAD_EVENT_TIMEOUT = 500 * 1000000LL; // 0.5sec + +// Log a warning when an event takes longer than this to process, even if an ANR does not occur. +const nsecs_t SLOW_EVENT_PROCESSING_WARNING_TIMEOUT = 2000 * 1000000LL; // 2sec static inline nsecs_t now() { @@ -176,12 +166,8 @@ static bool validateMotionEvent(int32_t action, size_t pointerCount, return true; } -static void scalePointerCoords(const PointerCoords* inCoords, size_t count, float scaleFactor, - PointerCoords* outCoords) { - for (size_t i = 0; i < count; i++) { - outCoords[i] = inCoords[i]; - outCoords[i].scale(scaleFactor); - } +static bool isMainDisplay(int32_t displayId) { + return displayId == ADISPLAY_ID_DEFAULT || displayId == ADISPLAY_ID_NONE; } static void dumpRegion(String8& dump, const SkRegion& region) { @@ -190,7 +176,6 @@ static void dumpRegion(String8& dump, const SkRegion& region) { return; } -#ifdef HAVE_ANDROID_OS bool first = true; for (SkRegion::Iterator it(region); !it.done(); it.next()) { if (first) { @@ -201,7 +186,6 @@ static void dumpRegion(String8& dump, const SkRegion& region) { const SkIRect& rect = it.rect(); dump.appendFormat("[%d,%d][%d,%d]", rect.fLeft, rect.fTop, rect.fRight, rect.fBottom); } -#endif } @@ -211,22 +195,13 @@ InputDispatcher::InputDispatcher(const sp& polic mPolicy(policy), mPendingEvent(NULL), mAppSwitchSawKeyDown(false), mAppSwitchDueTime(LONG_LONG_MAX), mNextUnblockedEvent(NULL), - mDispatchEnabled(true), mDispatchFrozen(false), mInputFilterEnabled(false), - mCurrentInputTargetsValid(false), + mDispatchEnabled(false), mDispatchFrozen(false), mInputFilterEnabled(false), mInputTargetWaitCause(INPUT_TARGET_WAIT_CAUSE_NONE) { mLooper = new Looper(false); mKeyRepeatState.lastKeyEntry = NULL; policy->getDispatcherConfiguration(&mConfig); - - mThrottleState.minTimeBetweenEvents = 1000000000LL / mConfig.maxEventsPerSecond; - mThrottleState.lastDeviceId = -1; - -#if DEBUG_THROTTLING - mThrottleState.originalSampleCount = 0; - ALOGD("Throttling - Max events per second = %d", mConfig.maxEventsPerSecond); -#endif } InputDispatcher::~InputDispatcher() { @@ -238,8 +213,8 @@ InputDispatcher::~InputDispatcher() { drainInboundQueueLocked(); } - while (mConnectionsByReceiveFd.size() != 0) { - unregisterInputChannel(mConnectionsByReceiveFd.valueAt(0)->inputChannel); + while (mConnectionsByFd.size() != 0) { + unregisterInputChannel(mConnectionsByFd.valueAt(0)->inputChannel); } } @@ -247,10 +222,18 @@ void InputDispatcher::dispatchOnce() { nsecs_t nextWakeupTime = LONG_LONG_MAX; { // acquire lock AutoMutex _l(mLock); - dispatchOnceInnerLocked(&nextWakeupTime); + mDispatcherIsAliveCondition.broadcast(); + // Run a dispatch loop if there are no pending commands. + // The dispatch loop might enqueue commands to run afterwards. + if (!haveCommandsLocked()) { + dispatchOnceInnerLocked(&nextWakeupTime); + } + + // Run all pending commands if there are any. + // If any commands were run then force the next poll to wake up immediately. if (runCommandsLockedInterruptible()) { - nextWakeupTime = LONG_LONG_MIN; // force next poll to wake up immediately + nextWakeupTime = LONG_LONG_MIN; } } // release lock @@ -309,78 +292,22 @@ void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) { } // Nothing to do if there is no pending event. - if (! mPendingEvent) { - if (mActiveConnections.isEmpty()) { - dispatchIdleLocked(); - } + if (!mPendingEvent) { return; } } else { // Inbound queue has at least one entry. - EventEntry* entry = mInboundQueue.head; - - // Throttle the entry if it is a move event and there are no - // other events behind it in the queue. Due to movement batching, additional - // samples may be appended to this event by the time the throttling timeout - // expires. - // TODO Make this smarter and consider throttling per device independently. - if (entry->type == EventEntry::TYPE_MOTION - && !isAppSwitchDue - && mDispatchEnabled - && (entry->policyFlags & POLICY_FLAG_PASS_TO_USER) - && !entry->isInjected()) { - MotionEntry* motionEntry = static_cast(entry); - int32_t deviceId = motionEntry->deviceId; - uint32_t source = motionEntry->source; - if (! isAppSwitchDue - && !motionEntry->next // exactly one event, no successors - && (motionEntry->action == AMOTION_EVENT_ACTION_MOVE - || motionEntry->action == AMOTION_EVENT_ACTION_HOVER_MOVE) - && deviceId == mThrottleState.lastDeviceId - && source == mThrottleState.lastSource) { - nsecs_t nextTime = mThrottleState.lastEventTime - + mThrottleState.minTimeBetweenEvents; - if (currentTime < nextTime) { - // Throttle it! -#if DEBUG_THROTTLING - ALOGD("Throttling - Delaying motion event for " - "device %d, source 0x%08x by up to %0.3fms.", - deviceId, source, (nextTime - currentTime) * 0.000001); -#endif - if (nextTime < *nextWakeupTime) { - *nextWakeupTime = nextTime; - } - if (mThrottleState.originalSampleCount == 0) { - mThrottleState.originalSampleCount = - motionEntry->countSamples(); - } - return; - } - } - -#if DEBUG_THROTTLING - if (mThrottleState.originalSampleCount != 0) { - uint32_t count = motionEntry->countSamples(); - ALOGD("Throttling - Motion event sample count grew by %d from %d to %d.", - count - mThrottleState.originalSampleCount, - mThrottleState.originalSampleCount, count); - mThrottleState.originalSampleCount = 0; - } -#endif - - mThrottleState.lastEventTime = currentTime; - mThrottleState.lastDeviceId = deviceId; - mThrottleState.lastSource = source; - } - - mInboundQueue.dequeue(entry); - mPendingEvent = entry; + mPendingEvent = mInboundQueue.dequeueAtHead(); + traceInboundQueueLengthLocked(); } // Poke user activity for this event. if (mPendingEvent->policyFlags & POLICY_FLAG_PASS_TO_USER) { pokeUserActivityLocked(mPendingEvent); } + + // Get ready to dispatch the event. + resetANRTimeoutsLocked(); } // Now we have an event to dispatch. @@ -468,19 +395,10 @@ void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) { } } -void InputDispatcher::dispatchIdleLocked() { -#if DEBUG_FOCUS - ALOGD("Dispatcher idle. There are no pending events or active connections."); -#endif - - // Reset targets when idle, to release input channels and other resources - // they are holding onto. - resetTargetsLocked(); -} - bool InputDispatcher::enqueueInboundEventLocked(EventEntry* entry) { bool needWake = mInboundQueue.isEmpty(); mInboundQueue.enqueueAtTail(entry); + traceInboundQueueLengthLocked(); switch (entry->type) { case EventEntry::TYPE_KEY: { @@ -515,11 +433,12 @@ bool InputDispatcher::enqueueInboundEventLocked(EventEntry* entry) { && (motionEntry->source & AINPUT_SOURCE_CLASS_POINTER) && mInputTargetWaitCause == INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY && mInputTargetWaitApplicationHandle != NULL) { - int32_t x = int32_t(motionEntry->firstSample.pointerCoords[0]. + int32_t displayId = motionEntry->displayId; + int32_t x = int32_t(motionEntry->pointerCoords[0]. getAxisValue(AMOTION_EVENT_AXIS_X)); - int32_t y = int32_t(motionEntry->firstSample.pointerCoords[0]. + int32_t y = int32_t(motionEntry->pointerCoords[0]. getAxisValue(AMOTION_EVENT_AXIS_Y)); - sp touchedWindowHandle = findTouchedWindowAtLocked(x, y); + sp touchedWindowHandle = findTouchedWindowAtLocked(displayId, x, y); if (touchedWindowHandle != NULL && touchedWindowHandle->inputApplicationHandle != mInputTargetWaitApplicationHandle) { @@ -536,28 +455,31 @@ bool InputDispatcher::enqueueInboundEventLocked(EventEntry* entry) { return needWake; } -sp InputDispatcher::findTouchedWindowAtLocked(int32_t x, int32_t y) { +sp InputDispatcher::findTouchedWindowAtLocked(int32_t displayId, + int32_t x, int32_t y) { // Traverse windows from front to back to find touched window. size_t numWindows = mWindowHandles.size(); for (size_t i = 0; i < numWindows; i++) { sp windowHandle = mWindowHandles.itemAt(i); const InputWindowInfo* windowInfo = windowHandle->getInfo(); - int32_t flags = windowInfo->layoutParamsFlags; + if (windowInfo->displayId == displayId) { + int32_t flags = windowInfo->layoutParamsFlags; - if (windowInfo->visible) { - if (!(flags & InputWindowInfo::FLAG_NOT_TOUCHABLE)) { - bool isTouchModal = (flags & (InputWindowInfo::FLAG_NOT_FOCUSABLE - | InputWindowInfo::FLAG_NOT_TOUCH_MODAL)) == 0; - if (isTouchModal || windowInfo->touchableRegionContainsPoint(x, y)) { - // Found window. - return windowHandle; + if (windowInfo->visible) { + if (!(flags & InputWindowInfo::FLAG_NOT_TOUCHABLE)) { + bool isTouchModal = (flags & (InputWindowInfo::FLAG_NOT_FOCUSABLE + | InputWindowInfo::FLAG_NOT_TOUCH_MODAL)) == 0; + if (isTouchModal || windowInfo->touchableRegionContainsPoint(x, y)) { + // Found window. + return windowHandle; + } } } - } - if (flags & InputWindowInfo::FLAG_SYSTEM_ERROR) { - // Error window is on top but not visible, so touch is dropped. - return NULL; + if (flags & InputWindowInfo::FLAG_SYSTEM_ERROR) { + // Error window is on top but not visible, so touch is dropped. + return NULL; + } } } return NULL; @@ -616,7 +538,9 @@ void InputDispatcher::dropInboundEventLocked(EventEntry* entry, DropReason dropR } bool InputDispatcher::isAppSwitchKeyCode(int32_t keyCode) { - return keyCode == AKEYCODE_HOME || keyCode == AKEYCODE_ENDCALL; + return keyCode == AKEYCODE_HOME + || keyCode == AKEYCODE_ENDCALL + || keyCode == AKEYCODE_APP_SWITCH; } bool InputDispatcher::isAppSwitchKeyEventLocked(KeyEntry* keyEntry) { @@ -646,6 +570,10 @@ bool InputDispatcher::isStaleEventLocked(nsecs_t currentTime, EventEntry* entry) return currentTime - entry->eventTime >= STALE_EVENT_TIMEOUT; } +bool InputDispatcher::haveCommandsLocked() const { + return !mCommandQueue.isEmpty(); +} + bool InputDispatcher::runCommandsLockedInterruptible() { if (mCommandQueue.isEmpty()) { return false; @@ -674,10 +602,12 @@ void InputDispatcher::drainInboundQueueLocked() { EventEntry* entry = mInboundQueue.dequeueAtHead(); releaseInboundEventLocked(entry); } + traceInboundQueueLengthLocked(); } void InputDispatcher::releasePendingEventLocked() { if (mPendingEvent) { + resetANRTimeoutsLocked(); releaseInboundEventLocked(mPendingEvent); mPendingEvent = NULL; } @@ -800,7 +730,6 @@ bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry, } entry->dispatchInProgress = true; - resetTargetsLocked(); logOutboundKeyDetailsLocked("dispatchKey - ", entry); } @@ -839,31 +768,28 @@ bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry, // Clean up if dropping the event. if (*dropReason != DROP_REASON_NOT_DROPPED) { - resetTargetsLocked(); setInjectionResultLocked(entry, *dropReason == DROP_REASON_POLICY ? INPUT_EVENT_INJECTION_SUCCEEDED : INPUT_EVENT_INJECTION_FAILED); return true; } // Identify targets. - if (! mCurrentInputTargetsValid) { - int32_t injectionResult = findFocusedWindowTargetsLocked(currentTime, - entry, nextWakeupTime); - if (injectionResult == INPUT_EVENT_INJECTION_PENDING) { - return false; - } - - setInjectionResultLocked(entry, injectionResult); - if (injectionResult != INPUT_EVENT_INJECTION_SUCCEEDED) { - return true; - } - - addMonitoringTargetsLocked(); - commitTargetsLocked(); + Vector inputTargets; + int32_t injectionResult = findFocusedWindowTargetsLocked(currentTime, + entry, inputTargets, nextWakeupTime); + if (injectionResult == INPUT_EVENT_INJECTION_PENDING) { + return false; } + setInjectionResultLocked(entry, injectionResult); + if (injectionResult != INPUT_EVENT_INJECTION_SUCCEEDED) { + return true; + } + + addMonitoringTargetsLocked(inputTargets); + // Dispatch the key. - dispatchEventToCurrentInputTargetsLocked(currentTime, entry, false); + dispatchEventLocked(currentTime, entry, inputTargets); return true; } @@ -884,14 +810,12 @@ bool InputDispatcher::dispatchMotionLocked( // Preprocessing. if (! entry->dispatchInProgress) { entry->dispatchInProgress = true; - resetTargetsLocked(); logOutboundMotionDetailsLocked("dispatchMotion - ", entry); } // Clean up if dropping the event. if (*dropReason != DROP_REASON_NOT_DROPPED) { - resetTargetsLocked(); setInjectionResultLocked(entry, *dropReason == DROP_REASON_POLICY ? INPUT_EVENT_INJECTION_SUCCEEDED : INPUT_EVENT_INJECTION_FAILED); return true; @@ -900,66 +824,31 @@ bool InputDispatcher::dispatchMotionLocked( bool isPointerEvent = entry->source & AINPUT_SOURCE_CLASS_POINTER; // Identify targets. + Vector inputTargets; + bool conflictingPointerActions = false; - if (! mCurrentInputTargetsValid) { - int32_t injectionResult; - const MotionSample* splitBatchAfterSample = NULL; - if (isPointerEvent) { - // Pointer event. (eg. touchscreen) - injectionResult = findTouchedWindowTargetsLocked(currentTime, - entry, nextWakeupTime, &conflictingPointerActions, &splitBatchAfterSample); - } else { - // Non touch event. (eg. trackball) - injectionResult = findFocusedWindowTargetsLocked(currentTime, - entry, nextWakeupTime); - } - if (injectionResult == INPUT_EVENT_INJECTION_PENDING) { - return false; - } + int32_t injectionResult; + if (isPointerEvent) { + // Pointer event. (eg. touchscreen) + injectionResult = findTouchedWindowTargetsLocked(currentTime, + entry, inputTargets, nextWakeupTime, &conflictingPointerActions); + } else { + // Non touch event. (eg. trackball) + injectionResult = findFocusedWindowTargetsLocked(currentTime, + entry, inputTargets, nextWakeupTime); + } + if (injectionResult == INPUT_EVENT_INJECTION_PENDING) { + return false; + } - setInjectionResultLocked(entry, injectionResult); - if (injectionResult != INPUT_EVENT_INJECTION_SUCCEEDED) { - return true; - } + setInjectionResultLocked(entry, injectionResult); + if (injectionResult != INPUT_EVENT_INJECTION_SUCCEEDED) { + return true; + } - addMonitoringTargetsLocked(); - commitTargetsLocked(); - - // Unbatch the event if necessary by splitting it into two parts after the - // motion sample indicated by splitBatchAfterSample. - if (splitBatchAfterSample && splitBatchAfterSample->next) { -#if DEBUG_BATCHING - uint32_t originalSampleCount = entry->countSamples(); -#endif - MotionSample* nextSample = splitBatchAfterSample->next; - MotionEntry* nextEntry = new MotionEntry(nextSample->eventTime, - entry->deviceId, entry->source, entry->policyFlags, - entry->action, entry->flags, - entry->metaState, entry->buttonState, entry->edgeFlags, - entry->xPrecision, entry->yPrecision, entry->downTime, - entry->pointerCount, entry->pointerProperties, nextSample->pointerCoords); - if (nextSample != entry->lastSample) { - nextEntry->firstSample.next = nextSample->next; - nextEntry->lastSample = entry->lastSample; - } - delete nextSample; - - entry->lastSample = const_cast(splitBatchAfterSample); - entry->lastSample->next = NULL; - - if (entry->injectionState) { - nextEntry->injectionState = entry->injectionState; - entry->injectionState->refCount += 1; - } - -#if DEBUG_BATCHING - ALOGD("Split batch of %d samples into two parts, first part has %d samples, " - "second part has %d samples.", originalSampleCount, - entry->countSamples(), nextEntry->countSamples()); -#endif - - mInboundQueue.enqueueAtHead(nextEntry); - } + // TODO: support sending secondary display events to input monitors + if (isMainDisplay(entry->displayId)) { + addMonitoringTargetsLocked(inputTargets); } // Dispatch the motion. @@ -968,7 +857,7 @@ bool InputDispatcher::dispatchMotionLocked( "conflicting pointer actions"); synthesizeCancelationEventsForAllConnectionsLocked(options); } - dispatchEventToCurrentInputTargetsLocked(currentTime, entry, false); + dispatchEventLocked(currentTime, entry, inputTargets); return true; } @@ -986,12 +875,6 @@ void InputDispatcher::logOutboundMotionDetailsLocked(const char* prefix, const M entry->edgeFlags, entry->xPrecision, entry->yPrecision, entry->downTime); - // Print the most recent sample that we have available, this may change due to batching. - size_t sampleCount = 1; - const MotionSample* sample = & entry->firstSample; - for (; sample->next != NULL; sample = sample->next) { - sampleCount += 1; - } for (uint32_t i = 0; i < entry->pointerCount; i++) { ALOGD(" Pointer %d: id=%d, toolType=%d, " "x=%f, y=%f, pressure=%f, size=%f, " @@ -999,45 +882,36 @@ void InputDispatcher::logOutboundMotionDetailsLocked(const char* prefix, const M "orientation=%f", i, entry->pointerProperties[i].id, entry->pointerProperties[i].toolType, - sample->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_X), - sample->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_Y), - sample->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE), - sample->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_SIZE), - sample->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR), - sample->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR), - sample->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR), - sample->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR), - sample->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION)); - } - - // Keep in mind that due to batching, it is possible for the number of samples actually - // dispatched to change before the application finally consumed them. - if (entry->action == AMOTION_EVENT_ACTION_MOVE) { - ALOGD(" ... Total movement samples currently batched %d ...", sampleCount); + entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_X), + entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_Y), + entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE), + entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_SIZE), + entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR), + entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR), + entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR), + entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR), + entry->pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION)); } #endif } -void InputDispatcher::dispatchEventToCurrentInputTargetsLocked(nsecs_t currentTime, - EventEntry* eventEntry, bool resumeWithAppendedMotionSample) { +void InputDispatcher::dispatchEventLocked(nsecs_t currentTime, + EventEntry* eventEntry, const Vector& inputTargets) { #if DEBUG_DISPATCH_CYCLE - ALOGD("dispatchEventToCurrentInputTargets - " - "resumeWithAppendedMotionSample=%s", - toString(resumeWithAppendedMotionSample)); + ALOGD("dispatchEventToCurrentInputTargets"); #endif ALOG_ASSERT(eventEntry->dispatchInProgress); // should already have been set to true pokeUserActivityLocked(eventEntry); - for (size_t i = 0; i < mCurrentInputTargets.size(); i++) { - const InputTarget& inputTarget = mCurrentInputTargets.itemAt(i); + for (size_t i = 0; i < inputTargets.size(); i++) { + const InputTarget& inputTarget = inputTargets.itemAt(i); ssize_t connectionIndex = getConnectionIndexLocked(inputTarget.inputChannel); if (connectionIndex >= 0) { - sp connection = mConnectionsByReceiveFd.valueAt(connectionIndex); - prepareDispatchCycleLocked(currentTime, connection, eventEntry, & inputTarget, - resumeWithAppendedMotionSample); + sp connection = mConnectionsByFd.valueAt(connectionIndex); + prepareDispatchCycleLocked(currentTime, connection, eventEntry, &inputTarget); } else { #if DEBUG_FOCUS ALOGD("Dropping event delivery to target with channel '%s' because it " @@ -1048,25 +922,15 @@ void InputDispatcher::dispatchEventToCurrentInputTargetsLocked(nsecs_t currentTi } } -void InputDispatcher::resetTargetsLocked() { - mCurrentInputTargetsValid = false; - mCurrentInputTargets.clear(); - resetANRTimeoutsLocked(); -} - -void InputDispatcher::commitTargetsLocked() { - mCurrentInputTargetsValid = true; -} - int32_t InputDispatcher::handleTargetsNotReadyLocked(nsecs_t currentTime, const EventEntry* entry, const sp& applicationHandle, const sp& windowHandle, - nsecs_t* nextWakeupTime) { + nsecs_t* nextWakeupTime, const char* reason) { if (applicationHandle == NULL && windowHandle == NULL) { if (mInputTargetWaitCause != INPUT_TARGET_WAIT_CAUSE_SYSTEM_NOT_READY) { #if DEBUG_FOCUS - ALOGD("Waiting for system to become ready for input."); + ALOGD("Waiting for system to become ready for input. Reason: %s", reason); #endif mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_SYSTEM_NOT_READY; mInputTargetWaitStartTime = currentTime; @@ -1077,8 +941,9 @@ int32_t InputDispatcher::handleTargetsNotReadyLocked(nsecs_t currentTime, } else { if (mInputTargetWaitCause != INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY) { #if DEBUG_FOCUS - ALOGD("Waiting for application to become ready for input: %s", - getApplicationWindowLabelLocked(applicationHandle, windowHandle).string()); + ALOGD("Waiting for application to become ready for input: %s. Reason: %s", + getApplicationWindowLabelLocked(applicationHandle, windowHandle).string(), + reason); #endif nsecs_t timeout; if (windowHandle != NULL) { @@ -1111,7 +976,7 @@ int32_t InputDispatcher::handleTargetsNotReadyLocked(nsecs_t currentTime, if (currentTime >= mInputTargetWaitTimeoutTime) { onANRLocked(currentTime, applicationHandle, windowHandle, - entry->eventTime, mInputTargetWaitStartTime); + entry->eventTime, mInputTargetWaitStartTime, reason); // Force poll loop to wake up immediately on next iteration once we get the // ANR response back from the policy. @@ -1135,14 +1000,17 @@ void InputDispatcher::resumeAfterTargetsNotReadyTimeoutLocked(nsecs_t newTimeout // Give up. mInputTargetWaitTimeoutExpired = true; - // Release the touch targets. - mTouchState.reset(); - // Input state will not be realistic. Mark it out of sync. if (inputChannel.get()) { ssize_t connectionIndex = getConnectionIndexLocked(inputChannel); if (connectionIndex >= 0) { - sp connection = mConnectionsByReceiveFd.valueAt(connectionIndex); + sp connection = mConnectionsByFd.valueAt(connectionIndex); + sp windowHandle = connection->inputWindowHandle; + + if (windowHandle != NULL) { + mTouchState.removeWindow(windowHandle); + } + if (connection->status == Connection::STATUS_NORMAL) { CancelationOptions options(CancelationOptions::CANCEL_ALL_EVENTS, "application not responding"); @@ -1172,22 +1040,18 @@ void InputDispatcher::resetANRTimeoutsLocked() { } int32_t InputDispatcher::findFocusedWindowTargetsLocked(nsecs_t currentTime, - const EventEntry* entry, nsecs_t* nextWakeupTime) { - mCurrentInputTargets.clear(); - + const EventEntry* entry, Vector& inputTargets, nsecs_t* nextWakeupTime) { int32_t injectionResult; // If there is no currently focused window and no focused application // then drop the event. if (mFocusedWindowHandle == NULL) { if (mFocusedApplicationHandle != NULL) { -#if DEBUG_FOCUS - ALOGD("Waiting because there is no focused window but there is a " - "focused application that may eventually add a window: %s.", - getApplicationWindowLabelLocked(mFocusedApplicationHandle, NULL).string()); -#endif injectionResult = handleTargetsNotReadyLocked(currentTime, entry, - mFocusedApplicationHandle, NULL, nextWakeupTime); + mFocusedApplicationHandle, NULL, nextWakeupTime, + "Waiting because no window has focus but there is a " + "focused application that may eventually add a window " + "when it finishes starting up."); goto Unresponsive; } @@ -1204,28 +1068,26 @@ int32_t InputDispatcher::findFocusedWindowTargetsLocked(nsecs_t currentTime, // If the currently focused window is paused then keep waiting. if (mFocusedWindowHandle->getInfo()->paused) { -#if DEBUG_FOCUS - ALOGD("Waiting because focused window is paused."); -#endif injectionResult = handleTargetsNotReadyLocked(currentTime, entry, - mFocusedApplicationHandle, mFocusedWindowHandle, nextWakeupTime); + mFocusedApplicationHandle, mFocusedWindowHandle, nextWakeupTime, + "Waiting because the focused window is paused."); goto Unresponsive; } // If the currently focused window is still working on previous events then keep waiting. - if (! isWindowFinishedWithPreviousInputLocked(mFocusedWindowHandle)) { -#if DEBUG_FOCUS - ALOGD("Waiting because focused window still processing previous input."); -#endif + if (!isWindowReadyForMoreInputLocked(currentTime, mFocusedWindowHandle, entry)) { injectionResult = handleTargetsNotReadyLocked(currentTime, entry, - mFocusedApplicationHandle, mFocusedWindowHandle, nextWakeupTime); + mFocusedApplicationHandle, mFocusedWindowHandle, nextWakeupTime, + "Waiting because the focused window has not finished " + "processing the input events that were previously delivered to it."); goto Unresponsive; } // Success! Output targets. injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED; addWindowTargetLocked(mFocusedWindowHandle, - InputTarget::FLAG_FOREGROUND | InputTarget::FLAG_DISPATCH_AS_IS, BitSet32(0)); + InputTarget::FLAG_FOREGROUND | InputTarget::FLAG_DISPATCH_AS_IS, BitSet32(0), + inputTargets); // Done. Failed: @@ -1235,23 +1097,21 @@ Unresponsive: injectionResult, timeSpentWaitingForApplication); #if DEBUG_FOCUS ALOGD("findFocusedWindow finished: injectionResult=%d, " - "timeSpendWaitingForApplication=%0.1fms", + "timeSpentWaitingForApplication=%0.1fms", injectionResult, timeSpentWaitingForApplication / 1000000.0); #endif return injectionResult; } int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, - const MotionEntry* entry, nsecs_t* nextWakeupTime, bool* outConflictingPointerActions, - const MotionSample** outSplitBatchAfterSample) { + const MotionEntry* entry, Vector& inputTargets, nsecs_t* nextWakeupTime, + bool* outConflictingPointerActions) { enum InjectionPermission { INJECTION_PERMISSION_UNKNOWN, INJECTION_PERMISSION_GRANTED, INJECTION_PERMISSION_DENIED }; - mCurrentInputTargets.clear(); - nsecs_t startTime = now(); // For security reasons, we defer updating the touch state until we are sure that @@ -1280,6 +1140,7 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, // bool screenWasOff = false; // original policy: policyFlags & POLICY_FLAG_BRIGHT_HERE; + int32_t displayId = entry->displayId; int32_t action = entry->action; int32_t maskedAction = action & AMOTION_EVENT_ACTION_MASK; @@ -1289,9 +1150,10 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, sp newHoverWindowHandle; bool isSplit = mTouchState.split; - bool switchedDevice = mTouchState.deviceId >= 0 + bool switchedDevice = mTouchState.deviceId >= 0 && mTouchState.displayId >= 0 && (mTouchState.deviceId != entry->deviceId - || mTouchState.source != entry->source); + || mTouchState.source != entry->source + || mTouchState.displayId != displayId); bool isHoverAction = (maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE || maskedAction == AMOTION_EVENT_ACTION_HOVER_ENTER || maskedAction == AMOTION_EVENT_ACTION_HOVER_EXIT); @@ -1315,6 +1177,7 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, mTempTouchState.down = down; mTempTouchState.deviceId = entry->deviceId; mTempTouchState.source = entry->source; + mTempTouchState.displayId = displayId; isSplit = false; } else { mTempTouchState.copyFrom(mTouchState); @@ -1323,11 +1186,10 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, if (newGesture || (isSplit && maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN)) { /* Case 1: New splittable pointer going down, or need target for hover or scroll. */ - const MotionSample* sample = &entry->firstSample; int32_t pointerIndex = getMotionEventActionPointerIndex(action); - int32_t x = int32_t(sample->pointerCoords[pointerIndex]. + int32_t x = int32_t(entry->pointerCoords[pointerIndex]. getAxisValue(AMOTION_EVENT_AXIS_X)); - int32_t y = int32_t(sample->pointerCoords[pointerIndex]. + int32_t y = int32_t(entry->pointerCoords[pointerIndex]. getAxisValue(AMOTION_EVENT_AXIS_Y)); sp newTouchedWindowHandle; sp topErrorWindowHandle; @@ -1338,8 +1200,11 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, for (size_t i = 0; i < numWindows; i++) { sp windowHandle = mWindowHandles.itemAt(i); const InputWindowInfo* windowInfo = windowHandle->getInfo(); - int32_t flags = windowInfo->layoutParamsFlags; + if (windowInfo->displayId != displayId) { + continue; // wrong display + } + int32_t flags = windowInfo->layoutParamsFlags; if (flags & InputWindowInfo::FLAG_SYSTEM_ERROR) { if (topErrorWindowHandle == NULL) { topErrorWindowHandle = windowHandle; @@ -1376,11 +1241,9 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, // it is invisible) then wait for it. Any other focused window may in // fact be in ANR state. if (topErrorWindowHandle != NULL && newTouchedWindowHandle != topErrorWindowHandle) { -#if DEBUG_FOCUS - ALOGD("Waiting because system error window is pending."); -#endif injectionResult = handleTargetsNotReadyLocked(currentTime, entry, - NULL, NULL, nextWakeupTime); + NULL, NULL, nextWakeupTime, + "Waiting because a system error window is about to be displayed."); injectionPermission = INJECTION_PERMISSION_UNKNOWN; goto Unresponsive; } @@ -1392,27 +1255,33 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, isSplit = true; } else if (isSplit) { // New window does not support splitting but we have already split events. - // Assign the pointer to the first foreground window we find. - // (May be NULL which is why we put this code block before the next check.) - newTouchedWindowHandle = mTempTouchState.getFirstForegroundWindowHandle(); + // Ignore the new window. + newTouchedWindowHandle = NULL; } - // If we did not find a touched window then fail. + // Handle the case where we did not find a window. if (newTouchedWindowHandle == NULL) { - if (mFocusedApplicationHandle != NULL) { -#if DEBUG_FOCUS - ALOGD("Waiting because there is no touched window but there is a " - "focused application that may eventually add a new window: %s.", - getApplicationWindowLabelLocked(mFocusedApplicationHandle, NULL).string()); -#endif - injectionResult = handleTargetsNotReadyLocked(currentTime, entry, - mFocusedApplicationHandle, NULL, nextWakeupTime); - goto Unresponsive; - } + // Try to assign the pointer to the first foreground window we find, if there is one. + newTouchedWindowHandle = mTempTouchState.getFirstForegroundWindowHandle(); + if (newTouchedWindowHandle == NULL) { + // There is no touched window. If this is an initial down event + // then wait for a window to appear that will handle the touch. This is + // to ensure that we report an ANR in the case where an application has started + // but not yet put up a window and the user is starting to get impatient. + if (maskedAction == AMOTION_EVENT_ACTION_DOWN + && mFocusedApplicationHandle != NULL) { + injectionResult = handleTargetsNotReadyLocked(currentTime, entry, + mFocusedApplicationHandle, NULL, nextWakeupTime, + "Waiting because there is no touchable window that can " + "handle the event but there is focused application that may " + "eventually add a new window when it finishes starting up."); + goto Unresponsive; + } - ALOGI("Dropping event because there is no touched window or focused application."); - injectionResult = INPUT_EVENT_INJECTION_FAILED; - goto Failed; + ALOGI("Dropping event because there is no touched window."); + injectionResult = INPUT_EVENT_INJECTION_FAILED; + goto Failed; + } } // Set target flags. @@ -1427,21 +1296,6 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, // Update hover state. if (isHoverAction) { newHoverWindowHandle = newTouchedWindowHandle; - - // Ensure all subsequent motion samples are also within the touched window. - // Set *outSplitBatchAfterSample to the sample before the first one that is not - // within the touched window. - if (!isTouchModal) { - while (sample->next) { - if (!newHoverWindowHandle->getInfo()->touchableRegionContainsPoint( - sample->next->pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X), - sample->next->pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y))) { - *outSplitBatchAfterSample = sample; - break; - } - sample = sample->next; - } - } } else if (maskedAction == AMOTION_EVENT_ACTION_SCROLL) { newHoverWindowHandle = mLastHoverWindowHandle; } @@ -1470,13 +1324,13 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, if (maskedAction == AMOTION_EVENT_ACTION_MOVE && entry->pointerCount == 1 && mTempTouchState.isSlippery()) { - const MotionSample* sample = &entry->firstSample; - int32_t x = int32_t(sample->pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X)); - int32_t y = int32_t(sample->pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y)); + int32_t x = int32_t(entry->pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X)); + int32_t y = int32_t(entry->pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y)); sp oldTouchedWindowHandle = mTempTouchState.getFirstForegroundWindowHandle(); - sp newTouchedWindowHandle = findTouchedWindowAtLocked(x, y); + sp newTouchedWindowHandle = + findTouchedWindowAtLocked(displayId, x, y); if (oldTouchedWindowHandle != newTouchedWindowHandle && newTouchedWindowHandle != NULL) { #if DEBUG_FOCUS @@ -1507,17 +1361,11 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, pointerIds.markBit(entry->pointerProperties[0].id); } mTempTouchState.addOrUpdateWindow(newTouchedWindowHandle, targetFlags, pointerIds); - - // Split the batch here so we send exactly one sample. - *outSplitBatchAfterSample = &entry->firstSample; } } } if (newHoverWindowHandle != mLastHoverWindowHandle) { - // Split the batch here so we send exactly one sample as part of ENTER or EXIT. - *outSplitBatchAfterSample = &entry->firstSample; - // Let the previous window know that the hover sequence is over. if (mLastHoverWindowHandle != NULL) { #if DEBUG_HOVER @@ -1591,21 +1439,18 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, if (touchedWindow.targetFlags & InputTarget::FLAG_FOREGROUND) { // If the touched window is paused then keep waiting. if (touchedWindow.windowHandle->getInfo()->paused) { -#if DEBUG_FOCUS - ALOGD("Waiting because touched window is paused."); -#endif injectionResult = handleTargetsNotReadyLocked(currentTime, entry, - NULL, touchedWindow.windowHandle, nextWakeupTime); + NULL, touchedWindow.windowHandle, nextWakeupTime, + "Waiting because the touched window is paused."); goto Unresponsive; } // If the touched window is still working on previous events then keep waiting. - if (! isWindowFinishedWithPreviousInputLocked(touchedWindow.windowHandle)) { -#if DEBUG_FOCUS - ALOGD("Waiting because touched window still processing previous input."); -#endif + if (!isWindowReadyForMoreInputLocked(currentTime, touchedWindow.windowHandle, entry)) { injectionResult = handleTargetsNotReadyLocked(currentTime, entry, - NULL, touchedWindow.windowHandle, nextWakeupTime); + NULL, touchedWindow.windowHandle, nextWakeupTime, + "Waiting because the touched window has not finished " + "processing the input events that were previously delivered to it."); goto Unresponsive; } } @@ -1623,8 +1468,10 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, if (foregroundWindowHandle->getInfo()->hasWallpaper) { for (size_t i = 0; i < mWindowHandles.size(); i++) { sp windowHandle = mWindowHandles.itemAt(i); - if (windowHandle->getInfo()->layoutParamsType - == InputWindowInfo::TYPE_WALLPAPER) { + const InputWindowInfo* info = windowHandle->getInfo(); + if (info->displayId == displayId + && windowHandle->getInfo()->layoutParamsType + == InputWindowInfo::TYPE_WALLPAPER) { mTempTouchState.addOrUpdateWindow(windowHandle, InputTarget::FLAG_WINDOW_IS_OBSCURED | InputTarget::FLAG_DISPATCH_AS_IS, @@ -1640,7 +1487,7 @@ int32_t InputDispatcher::findTouchedWindowTargetsLocked(nsecs_t currentTime, for (size_t i = 0; i < mTempTouchState.windows.size(); i++) { const TouchedWindow& touchedWindow = mTempTouchState.windows.itemAt(i); addWindowTargetLocked(touchedWindow.windowHandle, touchedWindow.targetFlags, - touchedWindow.pointerIds); + touchedWindow.pointerIds, inputTargets); } // Drop the outside or hover touch windows since we will not care about them @@ -1680,6 +1527,7 @@ Failed: || maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE) { mTouchState.deviceId = entry->deviceId; mTouchState.source = entry->source; + mTouchState.displayId = displayId; } } else if (maskedAction == AMOTION_EVENT_ACTION_UP || maskedAction == AMOTION_EVENT_ACTION_CANCEL) { @@ -1745,11 +1593,11 @@ Unresponsive: } void InputDispatcher::addWindowTargetLocked(const sp& windowHandle, - int32_t targetFlags, BitSet32 pointerIds) { - mCurrentInputTargets.push(); + int32_t targetFlags, BitSet32 pointerIds, Vector& inputTargets) { + inputTargets.push(); const InputWindowInfo* windowInfo = windowHandle->getInfo(); - InputTarget& target = mCurrentInputTargets.editTop(); + InputTarget& target = inputTargets.editTop(); target.inputChannel = windowInfo->inputChannel; target.flags = targetFlags; target.xOffset = - windowInfo->frameLeft; @@ -1758,11 +1606,11 @@ void InputDispatcher::addWindowTargetLocked(const sp& windowH target.pointerIds = pointerIds; } -void InputDispatcher::addMonitoringTargetsLocked() { +void InputDispatcher::addMonitoringTargetsLocked(Vector& inputTargets) { for (size_t i = 0; i < mMonitoringChannels.size(); i++) { - mCurrentInputTargets.push(); + inputTargets.push(); - InputTarget& target = mCurrentInputTargets.editTop(); + InputTarget& target = inputTargets.editTop(); target.inputChannel = mMonitoringChannels[i]; target.flags = InputTarget::FLAG_DISPATCH_AS_IS; target.xOffset = 0; @@ -1795,6 +1643,7 @@ bool InputDispatcher::checkInjectionPermission(const sp& wind bool InputDispatcher::isWindowObscuredAtPointLocked( const sp& windowHandle, int32_t x, int32_t y) const { + int32_t displayId = windowHandle->getInfo()->displayId; size_t numWindows = mWindowHandles.size(); for (size_t i = 0; i < numWindows; i++) { sp otherHandle = mWindowHandles.itemAt(i); @@ -1803,7 +1652,8 @@ bool InputDispatcher::isWindowObscuredAtPointLocked( } const InputWindowInfo* otherInfo = otherHandle->getInfo(); - if (otherInfo->visible && ! otherInfo->isTrustedOverlay() + if (otherInfo->displayId == displayId + && otherInfo->visible && !otherInfo->isTrustedOverlay() && otherInfo->frameContainsPoint(x, y)) { return true; } @@ -1811,15 +1661,51 @@ bool InputDispatcher::isWindowObscuredAtPointLocked( return false; } -bool InputDispatcher::isWindowFinishedWithPreviousInputLocked( - const sp& windowHandle) { +bool InputDispatcher::isWindowReadyForMoreInputLocked(nsecs_t currentTime, + const sp& windowHandle, const EventEntry* eventEntry) { ssize_t connectionIndex = getConnectionIndexLocked(windowHandle->getInputChannel()); if (connectionIndex >= 0) { - sp connection = mConnectionsByReceiveFd.valueAt(connectionIndex); - return connection->outboundQueue.isEmpty(); - } else { - return true; + sp connection = mConnectionsByFd.valueAt(connectionIndex); + if (connection->inputPublisherBlocked) { + return false; + } + if (eventEntry->type == EventEntry::TYPE_KEY) { + // If the event is a key event, then we must wait for all previous events to + // complete before delivering it because previous events may have the + // side-effect of transferring focus to a different window and we want to + // ensure that the following keys are sent to the new window. + // + // Suppose the user touches a button in a window then immediately presses "A". + // If the button causes a pop-up window to appear then we want to ensure that + // the "A" key is delivered to the new pop-up window. This is because users + // often anticipate pending UI changes when typing on a keyboard. + // To obtain this behavior, we must serialize key events with respect to all + // prior input events. + return connection->outboundQueue.isEmpty() + && connection->waitQueue.isEmpty(); + } + // Touch events can always be sent to a window immediately because the user intended + // to touch whatever was visible at the time. Even if focus changes or a new + // window appears moments later, the touch event was meant to be delivered to + // whatever window happened to be on screen at the time. + // + // Generic motion events, such as trackball or joystick events are a little trickier. + // Like key events, generic motion events are delivered to the focused window. + // Unlike key events, generic motion events don't tend to transfer focus to other + // windows and it is not important for them to be serialized. So we prefer to deliver + // generic motion events as soon as possible to improve efficiency and reduce lag + // through batching. + // + // The one case where we pause input event delivery is when the wait queue is piling + // up with lots of events because the application is not responding. + // This condition ensures that ANRs are detected reliably. + if (!connection->waitQueue.isEmpty() + && currentTime >= connection->waitQueue.head->eventEntry->eventTime + + STREAM_AHEAD_EVENT_TIMEOUT) { + return false; + } } + return true; } String8 InputDispatcher::getApplicationWindowLabelLocked( @@ -1842,7 +1728,17 @@ String8 InputDispatcher::getApplicationWindowLabelLocked( } void InputDispatcher::pokeUserActivityLocked(const EventEntry* eventEntry) { - int32_t eventType = POWER_MANAGER_OTHER_EVENT; + if (mFocusedWindowHandle != NULL) { + const InputWindowInfo* info = mFocusedWindowHandle->getInfo(); + if (info->inputFeatures & InputWindowInfo::INPUT_FEATURE_DISABLE_USER_ACTIVITY) { +#if DEBUG_DISPATCH_CYCLE + ALOGD("Not poking user activity: disabled by window '%s'.", info->name.string()); +#endif + return; + } + } + + int32_t eventType = USER_ACTIVITY_EVENT_OTHER; switch (eventEntry->type) { case EventEntry::TYPE_MOTION: { const MotionEntry* motionEntry = static_cast(eventEntry); @@ -1851,7 +1747,7 @@ void InputDispatcher::pokeUserActivityLocked(const EventEntry* eventEntry) { } if (MotionEvent::isTouchEvent(motionEntry->source, motionEntry->action)) { - eventType = POWER_MANAGER_TOUCH_EVENT; + eventType = USER_ACTIVITY_EVENT_TOUCH; } break; } @@ -1860,7 +1756,7 @@ void InputDispatcher::pokeUserActivityLocked(const EventEntry* eventEntry) { if (keyEntry->flags & AKEY_EVENT_FLAG_CANCELED) { return; } - eventType = POWER_MANAGER_BUTTON_EVENT; + eventType = USER_ACTIVITY_EVENT_BUTTON; break; } } @@ -1872,23 +1768,16 @@ void InputDispatcher::pokeUserActivityLocked(const EventEntry* eventEntry) { } void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime, - const sp& connection, EventEntry* eventEntry, const InputTarget* inputTarget, - bool resumeWithAppendedMotionSample) { + const sp& connection, EventEntry* eventEntry, const InputTarget* inputTarget) { #if DEBUG_DISPATCH_CYCLE ALOGD("channel '%s' ~ prepareDispatchCycle - flags=0x%08x, " "xOffset=%f, yOffset=%f, scaleFactor=%f, " - "pointerIds=0x%x, " - "resumeWithAppendedMotionSample=%s", + "pointerIds=0x%x", connection->getInputChannelName(), inputTarget->flags, inputTarget->xOffset, inputTarget->yOffset, - inputTarget->scaleFactor, inputTarget->pointerIds.value, - toString(resumeWithAppendedMotionSample)); + inputTarget->scaleFactor, inputTarget->pointerIds.value); #endif - // Make sure we are never called for streaming when splitting across multiple windows. - bool isSplit = inputTarget->flags & InputTarget::FLAG_SPLIT; - ALOG_ASSERT(! (resumeWithAppendedMotionSample && isSplit)); - // Skip this event if the connection status is not normal. // We don't want to enqueue additional outbound events if the connection is broken. if (connection->status != Connection::STATUS_NORMAL) { @@ -1900,7 +1789,7 @@ void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime, } // Split a motion event if needed. - if (isSplit) { + if (inputTarget->flags & InputTarget::FLAG_SPLIT) { ALOG_ASSERT(eventEntry->type == EventEntry::TYPE_MOTION); MotionEntry* originalMotionEntry = static_cast(eventEntry); @@ -1915,135 +1804,44 @@ void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime, connection->getInputChannelName()); logOutboundMotionDetailsLocked(" ", splitMotionEntry); #endif - eventEntry = splitMotionEntry; - } - } - - // Resume the dispatch cycle with a freshly appended motion sample. - // First we check that the last dispatch entry in the outbound queue is for the same - // motion event to which we appended the motion sample. If we find such a dispatch - // entry, and if it is currently in progress then we try to stream the new sample. - bool wasEmpty = connection->outboundQueue.isEmpty(); - - if (! wasEmpty && resumeWithAppendedMotionSample) { - DispatchEntry* motionEventDispatchEntry = - connection->findQueuedDispatchEntryForEvent(eventEntry); - if (motionEventDispatchEntry) { - // If the dispatch entry is not in progress, then we must be busy dispatching an - // earlier event. Not a problem, the motion event is on the outbound queue and will - // be dispatched later. - if (! motionEventDispatchEntry->inProgress) { -#if DEBUG_BATCHING - ALOGD("channel '%s' ~ Not streaming because the motion event has " - "not yet been dispatched. " - "(Waiting for earlier events to be consumed.)", - connection->getInputChannelName()); -#endif - return; - } - - // If the dispatch entry is in progress but it already has a tail of pending - // motion samples, then it must mean that the shared memory buffer filled up. - // Not a problem, when this dispatch cycle is finished, we will eventually start - // a new dispatch cycle to process the tail and that tail includes the newly - // appended motion sample. - if (motionEventDispatchEntry->tailMotionSample) { -#if DEBUG_BATCHING - ALOGD("channel '%s' ~ Not streaming because no new samples can " - "be appended to the motion event in this dispatch cycle. " - "(Waiting for next dispatch cycle to start.)", - connection->getInputChannelName()); -#endif - return; - } - - // If the motion event was modified in flight, then we cannot stream the sample. - if ((motionEventDispatchEntry->targetFlags & InputTarget::FLAG_DISPATCH_MASK) - != InputTarget::FLAG_DISPATCH_AS_IS) { -#if DEBUG_BATCHING - ALOGD("channel '%s' ~ Not streaming because the motion event was not " - "being dispatched as-is. " - "(Waiting for next dispatch cycle to start.)", - connection->getInputChannelName()); -#endif - return; - } - - // The dispatch entry is in progress and is still potentially open for streaming. - // Try to stream the new motion sample. This might fail if the consumer has already - // consumed the motion event (or if the channel is broken). - MotionEntry* motionEntry = static_cast(eventEntry); - MotionSample* appendedMotionSample = motionEntry->lastSample; - status_t status; - if (motionEventDispatchEntry->scaleFactor == 1.0f) { - status = connection->inputPublisher.appendMotionSample( - appendedMotionSample->eventTime, appendedMotionSample->pointerCoords); - } else { - PointerCoords scaledCoords[MAX_POINTERS]; - for (size_t i = 0; i < motionEntry->pointerCount; i++) { - scaledCoords[i] = appendedMotionSample->pointerCoords[i]; - scaledCoords[i].scale(motionEventDispatchEntry->scaleFactor); - } - status = connection->inputPublisher.appendMotionSample( - appendedMotionSample->eventTime, scaledCoords); - } - if (status == OK) { -#if DEBUG_BATCHING - ALOGD("channel '%s' ~ Successfully streamed new motion sample.", - connection->getInputChannelName()); -#endif - return; - } - -#if DEBUG_BATCHING - if (status == NO_MEMORY) { - ALOGD("channel '%s' ~ Could not append motion sample to currently " - "dispatched move event because the shared memory buffer is full. " - "(Waiting for next dispatch cycle to start.)", - connection->getInputChannelName()); - } else if (status == status_t(FAILED_TRANSACTION)) { - ALOGD("channel '%s' ~ Could not append motion sample to currently " - "dispatched move event because the event has already been consumed. " - "(Waiting for next dispatch cycle to start.)", - connection->getInputChannelName()); - } else { - ALOGD("channel '%s' ~ Could not append motion sample to currently " - "dispatched move event due to an error, status=%d. " - "(Waiting for next dispatch cycle to start.)", - connection->getInputChannelName(), status); - } -#endif - // Failed to stream. Start a new tail of pending motion samples to dispatch - // in the next cycle. - motionEventDispatchEntry->tailMotionSample = appendedMotionSample; + enqueueDispatchEntriesLocked(currentTime, connection, + splitMotionEntry, inputTarget); + splitMotionEntry->release(); return; } } + // Not splitting. Enqueue dispatch entries for the event as is. + enqueueDispatchEntriesLocked(currentTime, connection, eventEntry, inputTarget); +} + +void InputDispatcher::enqueueDispatchEntriesLocked(nsecs_t currentTime, + const sp& connection, EventEntry* eventEntry, const InputTarget* inputTarget) { + bool wasEmpty = connection->outboundQueue.isEmpty(); + // Enqueue dispatch entries for the requested modes. enqueueDispatchEntryLocked(connection, eventEntry, inputTarget, - resumeWithAppendedMotionSample, InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT); + InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT); enqueueDispatchEntryLocked(connection, eventEntry, inputTarget, - resumeWithAppendedMotionSample, InputTarget::FLAG_DISPATCH_AS_OUTSIDE); + InputTarget::FLAG_DISPATCH_AS_OUTSIDE); enqueueDispatchEntryLocked(connection, eventEntry, inputTarget, - resumeWithAppendedMotionSample, InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER); + InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER); enqueueDispatchEntryLocked(connection, eventEntry, inputTarget, - resumeWithAppendedMotionSample, InputTarget::FLAG_DISPATCH_AS_IS); + InputTarget::FLAG_DISPATCH_AS_IS); enqueueDispatchEntryLocked(connection, eventEntry, inputTarget, - resumeWithAppendedMotionSample, InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT); + InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT); enqueueDispatchEntryLocked(connection, eventEntry, inputTarget, - resumeWithAppendedMotionSample, InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER); + InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER); // If the outbound queue was previously empty, start the dispatch cycle going. if (wasEmpty && !connection->outboundQueue.isEmpty()) { - activateConnectionLocked(connection.get()); startDispatchCycleLocked(currentTime, connection); } } void InputDispatcher::enqueueDispatchEntryLocked( const sp& connection, EventEntry* eventEntry, const InputTarget* inputTarget, - bool resumeWithAppendedMotionSample, int32_t dispatchMode) { + int32_t dispatchMode) { int32_t inputTargetFlags = inputTarget->flags; if (!(inputTargetFlags & dispatchMode)) { return; @@ -2055,23 +1853,6 @@ void InputDispatcher::enqueueDispatchEntryLocked( DispatchEntry* dispatchEntry = new DispatchEntry(eventEntry, // increments ref inputTargetFlags, inputTarget->xOffset, inputTarget->yOffset, inputTarget->scaleFactor); - if (dispatchEntry->hasForegroundTarget()) { - incrementPendingForegroundDispatchesLocked(eventEntry); - } - - // Handle the case where we could not stream a new motion sample because the consumer has - // already consumed the motion event (otherwise the corresponding dispatch entry would - // still be in the outbound queue for this connection). We set the head motion sample - // to the list starting with the newly appended motion sample. - if (resumeWithAppendedMotionSample) { -#if DEBUG_BATCHING - ALOGD("channel '%s' ~ Preparing a new dispatch cycle for additional motion samples " - "that cannot be streamed because the motion event has already been consumed.", - connection->getInputChannelName()); -#endif - MotionSample* appendedMotionSample = static_cast(eventEntry)->lastSample; - dispatchEntry->headMotionSample = appendedMotionSample; - } // Apply target flags and update the connection's input state. switch (eventEntry->type) { @@ -2086,6 +1867,7 @@ void InputDispatcher::enqueueDispatchEntryLocked( ALOGD("channel '%s' ~ enqueueDispatchEntryLocked: skipping inconsistent key event", connection->getInputChannelName()); #endif + delete dispatchEntry; return; // skip the inconsistent event } break; @@ -2108,7 +1890,7 @@ void InputDispatcher::enqueueDispatchEntryLocked( } if (dispatchEntry->resolvedAction == AMOTION_EVENT_ACTION_HOVER_MOVE && !connection->inputState.isHovering( - motionEntry->deviceId, motionEntry->source)) { + motionEntry->deviceId, motionEntry->source, motionEntry->displayId)) { #if DEBUG_DISPATCH_CYCLE ALOGD("channel '%s' ~ enqueueDispatchEntryLocked: filling in missing hover enter event", connection->getInputChannelName()); @@ -2127,14 +1909,21 @@ void InputDispatcher::enqueueDispatchEntryLocked( ALOGD("channel '%s' ~ enqueueDispatchEntryLocked: skipping inconsistent motion event", connection->getInputChannelName()); #endif + delete dispatchEntry; return; // skip the inconsistent event } break; } } + // Remember that we are waiting for this dispatch to complete. + if (dispatchEntry->hasForegroundTarget()) { + incrementPendingForegroundDispatchesLocked(eventEntry); + } + // Enqueue the dispatch entry. connection->outboundQueue.enqueueAtTail(dispatchEntry); + traceOutboundQueueLengthLocked(connection); } void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, @@ -2144,227 +1933,131 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, connection->getInputChannelName()); #endif - ALOG_ASSERT(connection->status == Connection::STATUS_NORMAL); - ALOG_ASSERT(! connection->outboundQueue.isEmpty()); + while (connection->status == Connection::STATUS_NORMAL + && !connection->outboundQueue.isEmpty()) { + DispatchEntry* dispatchEntry = connection->outboundQueue.head; + dispatchEntry->deliveryTime = currentTime; - DispatchEntry* dispatchEntry = connection->outboundQueue.head; - ALOG_ASSERT(! dispatchEntry->inProgress); + // Publish the event. + status_t status; + EventEntry* eventEntry = dispatchEntry->eventEntry; + switch (eventEntry->type) { + case EventEntry::TYPE_KEY: { + KeyEntry* keyEntry = static_cast(eventEntry); - // Mark the dispatch entry as in progress. - dispatchEntry->inProgress = true; - - // Publish the event. - status_t status; - EventEntry* eventEntry = dispatchEntry->eventEntry; - switch (eventEntry->type) { - case EventEntry::TYPE_KEY: { - KeyEntry* keyEntry = static_cast(eventEntry); - - // Publish the key event. - status = connection->inputPublisher.publishKeyEvent( - keyEntry->deviceId, keyEntry->source, - dispatchEntry->resolvedAction, dispatchEntry->resolvedFlags, - keyEntry->keyCode, keyEntry->scanCode, - keyEntry->metaState, keyEntry->repeatCount, keyEntry->downTime, - keyEntry->eventTime); - - if (status) { - ALOGE("channel '%s' ~ Could not publish key event, " - "status=%d", connection->getInputChannelName(), status); - abortBrokenDispatchCycleLocked(currentTime, connection, true /*notify*/); - return; - } - break; - } - - case EventEntry::TYPE_MOTION: { - MotionEntry* motionEntry = static_cast(eventEntry); - - // If headMotionSample is non-NULL, then it points to the first new sample that we - // were unable to dispatch during the previous cycle so we resume dispatching from - // that point in the list of motion samples. - // Otherwise, we just start from the first sample of the motion event. - MotionSample* firstMotionSample = dispatchEntry->headMotionSample; - if (! firstMotionSample) { - firstMotionSample = & motionEntry->firstSample; + // Publish the key event. + status = connection->inputPublisher.publishKeyEvent(dispatchEntry->seq, + keyEntry->deviceId, keyEntry->source, + dispatchEntry->resolvedAction, dispatchEntry->resolvedFlags, + keyEntry->keyCode, keyEntry->scanCode, + keyEntry->metaState, keyEntry->repeatCount, keyEntry->downTime, + keyEntry->eventTime); + break; } - PointerCoords scaledCoords[MAX_POINTERS]; - const PointerCoords* usingCoords = firstMotionSample->pointerCoords; + case EventEntry::TYPE_MOTION: { + MotionEntry* motionEntry = static_cast(eventEntry); - // Set the X and Y offset depending on the input source. - float xOffset, yOffset, scaleFactor; - if (motionEntry->source & AINPUT_SOURCE_CLASS_POINTER - && !(dispatchEntry->targetFlags & InputTarget::FLAG_ZERO_COORDS)) { - scaleFactor = dispatchEntry->scaleFactor; - xOffset = dispatchEntry->xOffset * scaleFactor; - yOffset = dispatchEntry->yOffset * scaleFactor; - if (scaleFactor != 1.0f) { - for (size_t i = 0; i < motionEntry->pointerCount; i++) { - scaledCoords[i] = firstMotionSample->pointerCoords[i]; - scaledCoords[i].scale(scaleFactor); - } - usingCoords = scaledCoords; - } - } else { - xOffset = 0.0f; - yOffset = 0.0f; - scaleFactor = 1.0f; + PointerCoords scaledCoords[MAX_POINTERS]; + const PointerCoords* usingCoords = motionEntry->pointerCoords; - // We don't want the dispatch target to know. - if (dispatchEntry->targetFlags & InputTarget::FLAG_ZERO_COORDS) { - for (size_t i = 0; i < motionEntry->pointerCount; i++) { - scaledCoords[i].clear(); - } - usingCoords = scaledCoords; - } - } - - // Publish the motion event and the first motion sample. - status = connection->inputPublisher.publishMotionEvent( - motionEntry->deviceId, motionEntry->source, - dispatchEntry->resolvedAction, dispatchEntry->resolvedFlags, - motionEntry->edgeFlags, motionEntry->metaState, motionEntry->buttonState, - xOffset, yOffset, - motionEntry->xPrecision, motionEntry->yPrecision, - motionEntry->downTime, firstMotionSample->eventTime, - motionEntry->pointerCount, motionEntry->pointerProperties, - usingCoords); - - if (status) { - ALOGE("channel '%s' ~ Could not publish motion event, " - "status=%d", connection->getInputChannelName(), status); - abortBrokenDispatchCycleLocked(currentTime, connection, true /*notify*/); - return; - } - - if (dispatchEntry->resolvedAction == AMOTION_EVENT_ACTION_MOVE - || dispatchEntry->resolvedAction == AMOTION_EVENT_ACTION_HOVER_MOVE) { - // Append additional motion samples. - MotionSample* nextMotionSample = firstMotionSample->next; - for (; nextMotionSample != NULL; nextMotionSample = nextMotionSample->next) { - if (usingCoords == scaledCoords) { - if (!(dispatchEntry->targetFlags & InputTarget::FLAG_ZERO_COORDS)) { - for (size_t i = 0; i < motionEntry->pointerCount; i++) { - scaledCoords[i] = nextMotionSample->pointerCoords[i]; - scaledCoords[i].scale(scaleFactor); - } + // Set the X and Y offset depending on the input source. + float xOffset, yOffset, scaleFactor; + if ((motionEntry->source & AINPUT_SOURCE_CLASS_POINTER) + && !(dispatchEntry->targetFlags & InputTarget::FLAG_ZERO_COORDS)) { + scaleFactor = dispatchEntry->scaleFactor; + xOffset = dispatchEntry->xOffset * scaleFactor; + yOffset = dispatchEntry->yOffset * scaleFactor; + if (scaleFactor != 1.0f) { + for (size_t i = 0; i < motionEntry->pointerCount; i++) { + scaledCoords[i] = motionEntry->pointerCoords[i]; + scaledCoords[i].scale(scaleFactor); } - } else { - usingCoords = nextMotionSample->pointerCoords; + usingCoords = scaledCoords; } - status = connection->inputPublisher.appendMotionSample( - nextMotionSample->eventTime, usingCoords); - if (status == NO_MEMORY) { + } else { + xOffset = 0.0f; + yOffset = 0.0f; + scaleFactor = 1.0f; + + // We don't want the dispatch target to know. + if (dispatchEntry->targetFlags & InputTarget::FLAG_ZERO_COORDS) { + for (size_t i = 0; i < motionEntry->pointerCount; i++) { + scaledCoords[i].clear(); + } + usingCoords = scaledCoords; + } + } + + // Publish the motion event. + status = connection->inputPublisher.publishMotionEvent(dispatchEntry->seq, + motionEntry->deviceId, motionEntry->source, + dispatchEntry->resolvedAction, dispatchEntry->resolvedFlags, + motionEntry->edgeFlags, motionEntry->metaState, motionEntry->buttonState, + xOffset, yOffset, + motionEntry->xPrecision, motionEntry->yPrecision, + motionEntry->downTime, motionEntry->eventTime, + motionEntry->pointerCount, motionEntry->pointerProperties, + usingCoords); + break; + } + + default: + ALOG_ASSERT(false); + return; + } + + // Check the result. + if (status) { + if (status == WOULD_BLOCK) { + if (connection->waitQueue.isEmpty()) { + ALOGE("channel '%s' ~ Could not publish event because the pipe is full. " + "This is unexpected because the wait queue is empty, so the pipe " + "should be empty and we shouldn't have any problems writing an " + "event to it, status=%d", connection->getInputChannelName(), status); + abortBrokenDispatchCycleLocked(currentTime, connection, true /*notify*/); + } else { + // Pipe is full and we are waiting for the app to finish process some events + // before sending more events to it. #if DEBUG_DISPATCH_CYCLE - ALOGD("channel '%s' ~ Shared memory buffer full. Some motion samples will " - "be sent in the next dispatch cycle.", + ALOGD("channel '%s' ~ Could not publish event because the pipe is full, " + "waiting for the application to catch up", connection->getInputChannelName()); #endif - break; - } - if (status != OK) { - ALOGE("channel '%s' ~ Could not append motion sample " - "for a reason other than out of memory, status=%d", - connection->getInputChannelName(), status); - abortBrokenDispatchCycleLocked(currentTime, connection, true /*notify*/); - return; + connection->inputPublisherBlocked = true; } + } else { + ALOGE("channel '%s' ~ Could not publish event due to an unexpected error, " + "status=%d", connection->getInputChannelName(), status); + abortBrokenDispatchCycleLocked(currentTime, connection, true /*notify*/); } - - // Remember the next motion sample that we could not dispatch, in case we ran out - // of space in the shared memory buffer. - dispatchEntry->tailMotionSample = nextMotionSample; + return; } - break; - } - default: { - ALOG_ASSERT(false); + // Re-enqueue the event on the wait queue. + connection->outboundQueue.dequeue(dispatchEntry); + traceOutboundQueueLengthLocked(connection); + connection->waitQueue.enqueueAtTail(dispatchEntry); + traceWaitQueueLengthLocked(connection); } - } - - // Send the dispatch signal. - status = connection->inputPublisher.sendDispatchSignal(); - if (status) { - ALOGE("channel '%s' ~ Could not send dispatch signal, status=%d", - connection->getInputChannelName(), status); - abortBrokenDispatchCycleLocked(currentTime, connection, true /*notify*/); - return; - } - - // Record information about the newly started dispatch cycle. - connection->lastEventTime = eventEntry->eventTime; - connection->lastDispatchTime = currentTime; - - // Notify other system components. - onDispatchCycleStartedLocked(currentTime, connection); } void InputDispatcher::finishDispatchCycleLocked(nsecs_t currentTime, - const sp& connection, bool handled) { + const sp& connection, uint32_t seq, bool handled) { #if DEBUG_DISPATCH_CYCLE - ALOGD("channel '%s' ~ finishDispatchCycle - %01.1fms since event, " - "%01.1fms since dispatch, handled=%s", - connection->getInputChannelName(), - connection->getEventLatencyMillis(currentTime), - connection->getDispatchLatencyMillis(currentTime), - toString(handled)); + ALOGD("channel '%s' ~ finishDispatchCycle - seq=%u, handled=%s", + connection->getInputChannelName(), seq, toString(handled)); #endif + connection->inputPublisherBlocked = false; + if (connection->status == Connection::STATUS_BROKEN || connection->status == Connection::STATUS_ZOMBIE) { return; } - // Reset the publisher since the event has been consumed. - // We do this now so that the publisher can release some of its internal resources - // while waiting for the next dispatch cycle to begin. - status_t status = connection->inputPublisher.reset(); - if (status) { - ALOGE("channel '%s' ~ Could not reset publisher, status=%d", - connection->getInputChannelName(), status); - abortBrokenDispatchCycleLocked(currentTime, connection, true /*notify*/); - return; - } - // Notify other system components and prepare to start the next dispatch cycle. - onDispatchCycleFinishedLocked(currentTime, connection, handled); -} - -void InputDispatcher::startNextDispatchCycleLocked(nsecs_t currentTime, - const sp& connection) { - // Start the next dispatch cycle for this connection. - while (! connection->outboundQueue.isEmpty()) { - DispatchEntry* dispatchEntry = connection->outboundQueue.head; - if (dispatchEntry->inProgress) { - // Finish or resume current event in progress. - if (dispatchEntry->tailMotionSample) { - // We have a tail of undispatched motion samples. - // Reuse the same DispatchEntry and start a new cycle. - dispatchEntry->inProgress = false; - dispatchEntry->headMotionSample = dispatchEntry->tailMotionSample; - dispatchEntry->tailMotionSample = NULL; - startDispatchCycleLocked(currentTime, connection); - return; - } - // Finished. - connection->outboundQueue.dequeueAtHead(); - if (dispatchEntry->hasForegroundTarget()) { - decrementPendingForegroundDispatchesLocked(dispatchEntry->eventEntry); - } - delete dispatchEntry; - } else { - // If the head is not in progress, then we must have already dequeued the in - // progress event, which means we actually aborted it. - // So just start the next event for this connection. - startDispatchCycleLocked(currentTime, connection); - return; - } - } - - // Outbound queue is empty, deactivate the connection. - deactivateConnectionLocked(connection.get()); + onDispatchCycleFinishedLocked(currentTime, connection, seq, handled); } void InputDispatcher::abortBrokenDispatchCycleLocked(nsecs_t currentTime, @@ -2374,8 +2067,11 @@ void InputDispatcher::abortBrokenDispatchCycleLocked(nsecs_t currentTime, connection->getInputChannelName(), toString(notify)); #endif - // Clear the outbound queue. - drainOutboundQueueLocked(connection.get()); + // Clear the dispatch queues. + drainDispatchQueueLocked(&connection->outboundQueue); + traceOutboundQueueLengthLocked(connection); + drainDispatchQueueLocked(&connection->waitQueue); + traceWaitQueueLengthLocked(connection); // The connection appears to be unrecoverably broken. // Ignore already broken or zombie connections. @@ -2389,33 +2085,35 @@ void InputDispatcher::abortBrokenDispatchCycleLocked(nsecs_t currentTime, } } -void InputDispatcher::drainOutboundQueueLocked(Connection* connection) { - while (! connection->outboundQueue.isEmpty()) { - DispatchEntry* dispatchEntry = connection->outboundQueue.dequeueAtHead(); - if (dispatchEntry->hasForegroundTarget()) { - decrementPendingForegroundDispatchesLocked(dispatchEntry->eventEntry); - } - delete dispatchEntry; +void InputDispatcher::drainDispatchQueueLocked(Queue* queue) { + while (!queue->isEmpty()) { + DispatchEntry* dispatchEntry = queue->dequeueAtHead(); + releaseDispatchEntryLocked(dispatchEntry); } - - deactivateConnectionLocked(connection); } -int InputDispatcher::handleReceiveCallback(int receiveFd, int events, void* data) { +void InputDispatcher::releaseDispatchEntryLocked(DispatchEntry* dispatchEntry) { + if (dispatchEntry->hasForegroundTarget()) { + decrementPendingForegroundDispatchesLocked(dispatchEntry->eventEntry); + } + delete dispatchEntry; +} + +int InputDispatcher::handleReceiveCallback(int fd, int events, void* data) { InputDispatcher* d = static_cast(data); { // acquire lock AutoMutex _l(d->mLock); - ssize_t connectionIndex = d->mConnectionsByReceiveFd.indexOfKey(receiveFd); + ssize_t connectionIndex = d->mConnectionsByFd.indexOfKey(fd); if (connectionIndex < 0) { ALOGE("Received spurious receive callback for unknown input channel. " - "fd=%d, events=0x%x", receiveFd, events); + "fd=%d, events=0x%x", fd, events); return 0; // remove the callback } bool notify; - sp connection = d->mConnectionsByReceiveFd.valueAt(connectionIndex); + sp connection = d->mConnectionsByFd.valueAt(connectionIndex); if (!(events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP))) { if (!(events & ALOOPER_EVENT_INPUT)) { ALOGW("channel '%s' ~ Received spurious callback for unhandled poll event. " @@ -2423,18 +2121,31 @@ int InputDispatcher::handleReceiveCallback(int receiveFd, int events, void* data return 1; } - bool handled = false; - status_t status = connection->inputPublisher.receiveFinishedSignal(&handled); - if (!status) { - nsecs_t currentTime = now(); - d->finishDispatchCycleLocked(currentTime, connection, handled); + nsecs_t currentTime = now(); + bool gotOne = false; + status_t status; + for (;;) { + uint32_t seq; + bool handled; + status = connection->inputPublisher.receiveFinishedSignal(&seq, &handled); + if (status) { + break; + } + d->finishDispatchCycleLocked(currentTime, connection, seq, handled); + gotOne = true; + } + if (gotOne) { d->runCommandsLockedInterruptible(); - return 1; + if (status == WOULD_BLOCK) { + return 1; + } } - ALOGE("channel '%s' ~ Failed to receive finished signal. status=%d", - connection->getInputChannelName(), status); - notify = true; + notify = status != DEAD_OBJECT || !connection->monitor; + if (notify) { + ALOGE("channel '%s' ~ Failed to receive finished signal. status=%d", + connection->getInputChannelName(), status); + } } else { // Monitor channels are never explicitly unregistered. // We do it automatically when the remote endpoint is closed so don't warn @@ -2454,9 +2165,9 @@ int InputDispatcher::handleReceiveCallback(int receiveFd, int events, void* data void InputDispatcher::synthesizeCancelationEventsForAllConnectionsLocked( const CancelationOptions& options) { - for (size_t i = 0; i < mConnectionsByReceiveFd.size(); i++) { + for (size_t i = 0; i < mConnectionsByFd.size(); i++) { synthesizeCancelationEventsForConnectionLocked( - mConnectionsByReceiveFd.valueAt(i), options); + mConnectionsByFd.valueAt(i), options); } } @@ -2465,28 +2176,31 @@ void InputDispatcher::synthesizeCancelationEventsForInputChannelLocked( ssize_t index = getConnectionIndexLocked(channel); if (index >= 0) { synthesizeCancelationEventsForConnectionLocked( - mConnectionsByReceiveFd.valueAt(index), options); + mConnectionsByFd.valueAt(index), options); } } void InputDispatcher::synthesizeCancelationEventsForConnectionLocked( const sp& connection, const CancelationOptions& options) { + if (connection->status == Connection::STATUS_BROKEN) { + return; + } + nsecs_t currentTime = now(); - mTempCancelationEvents.clear(); + Vector cancelationEvents; connection->inputState.synthesizeCancelationEvents(currentTime, - mTempCancelationEvents, options); + cancelationEvents, options); - if (! mTempCancelationEvents.isEmpty() - && connection->status != Connection::STATUS_BROKEN) { + if (!cancelationEvents.isEmpty()) { #if DEBUG_OUTBOUND_EVENT_DETAILS ALOGD("channel '%s' ~ Synthesized %d cancelation events to bring channel back in sync " "with reality: %s, mode=%d.", - connection->getInputChannelName(), mTempCancelationEvents.size(), + connection->getInputChannelName(), cancelationEvents.size(), options.reason, options.mode); #endif - for (size_t i = 0; i < mTempCancelationEvents.size(); i++) { - EventEntry* cancelationEventEntry = mTempCancelationEvents.itemAt(i); + for (size_t i = 0; i < cancelationEvents.size(); i++) { + EventEntry* cancelationEventEntry = cancelationEvents.itemAt(i); switch (cancelationEventEntry->type) { case EventEntry::TYPE_KEY: logOutboundKeyDetailsLocked("cancel - ", @@ -2514,14 +2228,12 @@ void InputDispatcher::synthesizeCancelationEventsForConnectionLocked( target.flags = InputTarget::FLAG_DISPATCH_AS_IS; enqueueDispatchEntryLocked(connection, cancelationEventEntry, // increments ref - &target, false, InputTarget::FLAG_DISPATCH_AS_IS); + &target, InputTarget::FLAG_DISPATCH_AS_IS); cancelationEventEntry->release(); } - if (!connection->outboundQueue.head->inProgress) { - startDispatchCycleLocked(currentTime, connection); - } + startDispatchCycleLocked(currentTime, connection); } } @@ -2545,7 +2257,7 @@ InputDispatcher::splitMotionEvent(const MotionEntry* originalMotionEntry, BitSet splitPointerIndexMap[splitPointerCount] = originalPointerIndex; splitPointerProperties[splitPointerCount].copyFrom(pointerProperties); splitPointerCoords[splitPointerCount].copyFrom( - originalMotionEntry->firstSample.pointerCoords[originalPointerIndex]); + originalMotionEntry->pointerCoords[originalPointerIndex]); splitPointerCount += 1; } } @@ -2604,20 +2316,9 @@ InputDispatcher::splitMotionEvent(const MotionEntry* originalMotionEntry, BitSet originalMotionEntry->xPrecision, originalMotionEntry->yPrecision, originalMotionEntry->downTime, + originalMotionEntry->displayId, splitPointerCount, splitPointerProperties, splitPointerCoords); - for (MotionSample* originalMotionSample = originalMotionEntry->firstSample.next; - originalMotionSample != NULL; originalMotionSample = originalMotionSample->next) { - for (uint32_t splitPointerIndex = 0; splitPointerIndex < splitPointerCount; - splitPointerIndex++) { - uint32_t originalPointerIndex = splitPointerIndexMap[splitPointerIndex]; - splitPointerCoords[splitPointerIndex].copyFrom( - originalMotionSample->pointerCoords[originalPointerIndex]); - } - - splitMotionEntry->appendSample(originalMotionSample->eventTime, splitPointerCoords); - } - if (originalMotionEntry->injectionState) { splitMotionEntry->injectionState = originalMotionEntry->injectionState; splitMotionEntry->injectionState->refCount += 1; @@ -2696,7 +2397,7 @@ void InputDispatcher::notifyKey(const NotifyKeyArgs* args) { { // acquire lock mLock.lock(); - if (mInputFilterEnabled) { + if (shouldSendKeyToInputFilterLocked(args)) { mLock.unlock(); policyFlags |= POLICY_FLAG_FILTERED; @@ -2722,6 +2423,10 @@ void InputDispatcher::notifyKey(const NotifyKeyArgs* args) { } } +bool InputDispatcher::shouldSendKeyToInputFilterLocked(const NotifyKeyArgs* args) { + return mInputFilterEnabled; +} + void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) { #if DEBUG_INBOUND_EVENT_DETAILS ALOGD("notifyMotion - eventTime=%lld, deviceId=%d, source=0x%x, policyFlags=0x%x, " @@ -2760,7 +2465,7 @@ void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) { { // acquire lock mLock.lock(); - if (mInputFilterEnabled) { + if (shouldSendMotionToInputFilterLocked(args)) { mLock.unlock(); MotionEvent event; @@ -2778,168 +2483,12 @@ void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) { mLock.lock(); } - // Attempt batching and streaming of move events. - if (args->action == AMOTION_EVENT_ACTION_MOVE - || args->action == AMOTION_EVENT_ACTION_HOVER_MOVE) { - // BATCHING CASE - // - // Try to append a move sample to the tail of the inbound queue for this device. - // Give up if we encounter a non-move motion event for this device since that - // means we cannot append any new samples until a new motion event has started. - for (EventEntry* entry = mInboundQueue.tail; entry; entry = entry->prev) { - if (entry->type != EventEntry::TYPE_MOTION) { - // Keep looking for motion events. - continue; - } - - MotionEntry* motionEntry = static_cast(entry); - if (motionEntry->deviceId != args->deviceId - || motionEntry->source != args->source) { - // Keep looking for this device and source. - continue; - } - - if (!motionEntry->canAppendSamples(args->action, - args->pointerCount, args->pointerProperties)) { - // Last motion event in the queue for this device and source is - // not compatible for appending new samples. Stop here. - goto NoBatchingOrStreaming; - } - - // Do the batching magic. - batchMotionLocked(motionEntry, args->eventTime, - args->metaState, args->pointerCoords, - "most recent motion event for this device and source in the inbound queue"); - mLock.unlock(); - return; // done! - } - - // BATCHING ONTO PENDING EVENT CASE - // - // Try to append a move sample to the currently pending event, if there is one. - // We can do this as long as we are still waiting to find the targets for the - // event. Once the targets are locked-in we can only do streaming. - if (mPendingEvent - && (!mPendingEvent->dispatchInProgress || !mCurrentInputTargetsValid) - && mPendingEvent->type == EventEntry::TYPE_MOTION) { - MotionEntry* motionEntry = static_cast(mPendingEvent); - if (motionEntry->deviceId == args->deviceId - && motionEntry->source == args->source) { - if (!motionEntry->canAppendSamples(args->action, - args->pointerCount, args->pointerProperties)) { - // Pending motion event is for this device and source but it is - // not compatible for appending new samples. Stop here. - goto NoBatchingOrStreaming; - } - - // Do the batching magic. - batchMotionLocked(motionEntry, args->eventTime, - args->metaState, args->pointerCoords, - "pending motion event"); - mLock.unlock(); - return; // done! - } - } - - // STREAMING CASE - // - // There is no pending motion event (of any kind) for this device in the inbound queue. - // Search the outbound queue for the current foreground targets to find a dispatched - // motion event that is still in progress. If found, then, appen the new sample to - // that event and push it out to all current targets. The logic in - // prepareDispatchCycleLocked takes care of the case where some targets may - // already have consumed the motion event by starting a new dispatch cycle if needed. - if (mCurrentInputTargetsValid) { - for (size_t i = 0; i < mCurrentInputTargets.size(); i++) { - const InputTarget& inputTarget = mCurrentInputTargets[i]; - if ((inputTarget.flags & InputTarget::FLAG_FOREGROUND) == 0) { - // Skip non-foreground targets. We only want to stream if there is at - // least one foreground target whose dispatch is still in progress. - continue; - } - - ssize_t connectionIndex = getConnectionIndexLocked(inputTarget.inputChannel); - if (connectionIndex < 0) { - // Connection must no longer be valid. - continue; - } - - sp connection = mConnectionsByReceiveFd.valueAt(connectionIndex); - if (connection->outboundQueue.isEmpty()) { - // This foreground target has an empty outbound queue. - continue; - } - - DispatchEntry* dispatchEntry = connection->outboundQueue.head; - if (! dispatchEntry->inProgress - || dispatchEntry->eventEntry->type != EventEntry::TYPE_MOTION - || dispatchEntry->isSplit()) { - // No motion event is being dispatched, or it is being split across - // windows in which case we cannot stream. - continue; - } - - MotionEntry* motionEntry = static_cast( - dispatchEntry->eventEntry); - if (motionEntry->action != args->action - || motionEntry->deviceId != args->deviceId - || motionEntry->source != args->source - || motionEntry->pointerCount != args->pointerCount - || motionEntry->isInjected()) { - // The motion event is not compatible with this move. - continue; - } - - if (args->action == AMOTION_EVENT_ACTION_HOVER_MOVE) { - if (mLastHoverWindowHandle == NULL) { -#if DEBUG_BATCHING - ALOGD("Not streaming hover move because there is no " - "last hovered window."); -#endif - goto NoBatchingOrStreaming; - } - - sp hoverWindowHandle = findTouchedWindowAtLocked( - args->pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X), - args->pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y)); - if (mLastHoverWindowHandle != hoverWindowHandle) { -#if DEBUG_BATCHING - ALOGD("Not streaming hover move because the last hovered window " - "is '%s' but the currently hovered window is '%s'.", - mLastHoverWindowHandle->getName().string(), - hoverWindowHandle != NULL - ? hoverWindowHandle->getName().string() : ""); -#endif - goto NoBatchingOrStreaming; - } - } - - // Hurray! This foreground target is currently dispatching a move event - // that we can stream onto. Append the motion sample and resume dispatch. - motionEntry->appendSample(args->eventTime, args->pointerCoords); -#if DEBUG_BATCHING - ALOGD("Appended motion sample onto batch for most recently dispatched " - "motion event for this device and source in the outbound queues. " - "Attempting to stream the motion sample."); -#endif - nsecs_t currentTime = now(); - dispatchEventToCurrentInputTargetsLocked(currentTime, motionEntry, - true /*resumeWithAppendedMotionSample*/); - - runCommandsLockedInterruptible(); - mLock.unlock(); - return; // done! - } - } - -NoBatchingOrStreaming:; - } - // Just enqueue a new motion event. MotionEntry* newEntry = new MotionEntry(args->eventTime, args->deviceId, args->source, policyFlags, args->action, args->flags, args->metaState, args->buttonState, args->edgeFlags, args->xPrecision, args->yPrecision, args->downTime, + args->displayId, args->pointerCount, args->pointerProperties, args->pointerCoords); needWake = enqueueInboundEventLocked(newEntry); @@ -2951,47 +2500,22 @@ NoBatchingOrStreaming:; } } -void InputDispatcher::batchMotionLocked(MotionEntry* entry, nsecs_t eventTime, - int32_t metaState, const PointerCoords* pointerCoords, const char* eventDescription) { - // Combine meta states. - entry->metaState |= metaState; - - // Coalesce this sample if not enough time has elapsed since the last sample was - // initially appended to the batch. - MotionSample* lastSample = entry->lastSample; - long interval = eventTime - lastSample->eventTimeBeforeCoalescing; - if (interval <= MOTION_SAMPLE_COALESCE_INTERVAL) { - uint32_t pointerCount = entry->pointerCount; - for (uint32_t i = 0; i < pointerCount; i++) { - lastSample->pointerCoords[i].copyFrom(pointerCoords[i]); - } - lastSample->eventTime = eventTime; -#if DEBUG_BATCHING - ALOGD("Coalesced motion into last sample of batch for %s, events were %0.3f ms apart", - eventDescription, interval * 0.000001f); -#endif - return; - } - - // Append the sample. - entry->appendSample(eventTime, pointerCoords); -#if DEBUG_BATCHING - ALOGD("Appended motion sample onto batch for %s, events were %0.3f ms apart", - eventDescription, interval * 0.000001f); -#endif +bool InputDispatcher::shouldSendMotionToInputFilterLocked(const NotifyMotionArgs* args) { + // TODO: support sending secondary display events to input filter + return mInputFilterEnabled && isMainDisplay(args->displayId); } void InputDispatcher::notifySwitch(const NotifySwitchArgs* args) { #if DEBUG_INBOUND_EVENT_DETAILS - ALOGD("notifySwitch - eventTime=%lld, policyFlags=0x%x, switchCode=%d, switchValue=%d", + ALOGD("notifySwitch - eventTime=%lld, policyFlags=0x%x, switchValues=0x%08x, switchMask=0x%08x", args->eventTime, args->policyFlags, - args->switchCode, args->switchValue); + args->switchValues, args->switchMask); #endif uint32_t policyFlags = args->policyFlags; policyFlags |= POLICY_FLAG_TRUSTED; mPolicy->notifySwitch(args->eventTime, - args->switchCode, args->switchValue, policyFlags); + args->switchValues, args->switchMask, policyFlags); } void InputDispatcher::notifyDeviceReset(const NotifyDeviceResetArgs* args) { @@ -3029,7 +2553,8 @@ int32_t InputDispatcher::injectInputEvent(const InputEvent* event, policyFlags |= POLICY_FLAG_TRUSTED; } - EventEntry* injectedEntry; + EventEntry* firstInjectedEntry; + EventEntry* lastInjectedEntry; switch (event->getType()) { case AINPUT_EVENT_TYPE_KEY: { const KeyEvent* keyEvent = static_cast(event); @@ -3052,16 +2577,18 @@ int32_t InputDispatcher::injectInputEvent(const InputEvent* event, } mLock.lock(); - injectedEntry = new KeyEntry(keyEvent->getEventTime(), + firstInjectedEntry = new KeyEntry(keyEvent->getEventTime(), keyEvent->getDeviceId(), keyEvent->getSource(), policyFlags, action, flags, keyEvent->getKeyCode(), keyEvent->getScanCode(), keyEvent->getMetaState(), keyEvent->getRepeatCount(), keyEvent->getDownTime()); + lastInjectedEntry = firstInjectedEntry; break; } case AINPUT_EVENT_TYPE_MOTION: { const MotionEvent* motionEvent = static_cast(event); + int32_t displayId = ADISPLAY_ID_DEFAULT; int32_t action = motionEvent->getAction(); size_t pointerCount = motionEvent->getPointerCount(); const PointerProperties* pointerProperties = motionEvent->getPointerProperties(); @@ -3077,20 +2604,29 @@ int32_t InputDispatcher::injectInputEvent(const InputEvent* event, mLock.lock(); const nsecs_t* sampleEventTimes = motionEvent->getSampleEventTimes(); const PointerCoords* samplePointerCoords = motionEvent->getSamplePointerCoords(); - MotionEntry* motionEntry = new MotionEntry(*sampleEventTimes, + firstInjectedEntry = new MotionEntry(*sampleEventTimes, motionEvent->getDeviceId(), motionEvent->getSource(), policyFlags, action, motionEvent->getFlags(), motionEvent->getMetaState(), motionEvent->getButtonState(), motionEvent->getEdgeFlags(), motionEvent->getXPrecision(), motionEvent->getYPrecision(), - motionEvent->getDownTime(), uint32_t(pointerCount), - pointerProperties, samplePointerCoords); + motionEvent->getDownTime(), displayId, + uint32_t(pointerCount), pointerProperties, samplePointerCoords); + lastInjectedEntry = firstInjectedEntry; for (size_t i = motionEvent->getHistorySize(); i > 0; i--) { sampleEventTimes += 1; samplePointerCoords += pointerCount; - motionEntry->appendSample(*sampleEventTimes, samplePointerCoords); + MotionEntry* nextInjectedEntry = new MotionEntry(*sampleEventTimes, + motionEvent->getDeviceId(), motionEvent->getSource(), policyFlags, + action, motionEvent->getFlags(), + motionEvent->getMetaState(), motionEvent->getButtonState(), + motionEvent->getEdgeFlags(), + motionEvent->getXPrecision(), motionEvent->getYPrecision(), + motionEvent->getDownTime(), displayId, + uint32_t(pointerCount), pointerProperties, samplePointerCoords); + lastInjectedEntry->next = nextInjectedEntry; + lastInjectedEntry = nextInjectedEntry; } - injectedEntry = motionEntry; break; } @@ -3105,9 +2641,15 @@ int32_t InputDispatcher::injectInputEvent(const InputEvent* event, } injectionState->refCount += 1; - injectedEntry->injectionState = injectionState; + lastInjectedEntry->injectionState = injectionState; + + bool needWake = false; + for (EventEntry* entry = firstInjectedEntry; entry != NULL; ) { + EventEntry* nextEntry = entry->next; + needWake |= enqueueInboundEventLocked(entry); + entry = nextEntry; + } - bool needWake = enqueueInboundEventLocked(injectedEntry); mLock.unlock(); if (needWake) { @@ -3355,13 +2897,13 @@ void InputDispatcher::setFocusedApplication( if (inputApplicationHandle != NULL && inputApplicationHandle->updateInfo()) { if (mFocusedApplicationHandle != inputApplicationHandle) { if (mFocusedApplicationHandle != NULL) { - resetTargetsLocked(); + resetANRTimeoutsLocked(); mFocusedApplicationHandle->releaseInfo(); } mFocusedApplicationHandle = inputApplicationHandle; } } else if (mFocusedApplicationHandle != NULL) { - resetTargetsLocked(); + resetANRTimeoutsLocked(); mFocusedApplicationHandle->releaseInfo(); mFocusedApplicationHandle.clear(); } @@ -3454,6 +2996,12 @@ bool InputDispatcher::transferTouchFocus(const sp& fromChannel, #endif return true; } + if (fromWindowHandle->getInfo()->displayId != toWindowHandle->getInfo()->displayId) { +#if DEBUG_FOCUS + ALOGD("Cannot transfer focus because windows are on different displays."); +#endif + return false; + } bool found = false; for (size_t i = 0; i < mTouchState.windows.size(); i++) { @@ -3484,8 +3032,8 @@ bool InputDispatcher::transferTouchFocus(const sp& fromChannel, ssize_t fromConnectionIndex = getConnectionIndexLocked(fromChannel); ssize_t toConnectionIndex = getConnectionIndexLocked(toChannel); if (fromConnectionIndex >= 0 && toConnectionIndex >= 0) { - sp fromConnection = mConnectionsByReceiveFd.valueAt(fromConnectionIndex); - sp toConnection = mConnectionsByReceiveFd.valueAt(toConnectionIndex); + sp fromConnection = mConnectionsByFd.valueAt(fromConnectionIndex); + sp toConnection = mConnectionsByFd.valueAt(toConnectionIndex); fromConnection->inputState.copyPointerStateTo(toConnection->inputState); CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS, @@ -3514,7 +3062,7 @@ void InputDispatcher::resetAndDropEverythingLocked(const char* reason) { resetKeyRepeatLocked(); releasePendingEventLocked(); drainInboundQueueLocked(); - resetTargetsLocked(); + resetANRTimeoutsLocked(); mTouchState.reset(); mLastHoverWindowHandle.clear(); @@ -3555,6 +3103,7 @@ void InputDispatcher::dumpDispatchStateLocked(String8& dump) { dump.appendFormat(INDENT "TouchSplit: %s\n", toString(mTouchState.split)); dump.appendFormat(INDENT "TouchDeviceId: %d\n", mTouchState.deviceId); dump.appendFormat(INDENT "TouchSource: 0x%08x\n", mTouchState.source); + dump.appendFormat(INDENT "TouchDisplayId: %d\n", mTouchState.displayId); if (!mTouchState.windows.isEmpty()) { dump.append(INDENT "TouchedWindows:\n"); for (size_t i = 0; i < mTouchState.windows.size(); i++) { @@ -3574,11 +3123,12 @@ void InputDispatcher::dumpDispatchStateLocked(String8& dump) { const sp& windowHandle = mWindowHandles.itemAt(i); const InputWindowInfo* windowInfo = windowHandle->getInfo(); - dump.appendFormat(INDENT2 "%d: name='%s', paused=%s, hasFocus=%s, hasWallpaper=%s, " + dump.appendFormat(INDENT2 "%d: name='%s', displayId=%d, " + "paused=%s, hasFocus=%s, hasWallpaper=%s, " "visible=%s, canReceiveKeys=%s, flags=0x%08x, type=0x%08x, layer=%d, " "frame=[%d,%d][%d,%d], scale=%f, " "touchableRegion=", - i, windowInfo->name.string(), + i, windowInfo->name.string(), windowInfo->displayId, toString(windowInfo->paused), toString(windowInfo->hasFocus), toString(windowInfo->hasWallpaper), @@ -3589,9 +3139,7 @@ void InputDispatcher::dumpDispatchStateLocked(String8& dump) { windowInfo->frameLeft, windowInfo->frameTop, windowInfo->frameRight, windowInfo->frameBottom, windowInfo->scaleFactor); -#ifdef HAVE_ANDROID_OS dumpRegion(dump, windowInfo->touchableRegion); -#endif dump.appendFormat(", inputFeatures=0x%08x", windowInfo->inputFeatures); dump.appendFormat(", ownerPid=%d, ownerUid=%d, dispatchingTimeout=%0.3fms\n", windowInfo->ownerPid, windowInfo->ownerUid, @@ -3611,28 +3159,78 @@ void InputDispatcher::dumpDispatchStateLocked(String8& dump) { dump.append(INDENT "MonitoringChannels: \n"); } - dump.appendFormat(INDENT "InboundQueue: length=%u\n", mInboundQueue.count()); + nsecs_t currentTime = now(); - if (!mActiveConnections.isEmpty()) { - dump.append(INDENT "ActiveConnections:\n"); - for (size_t i = 0; i < mActiveConnections.size(); i++) { - const Connection* connection = mActiveConnections[i]; - dump.appendFormat(INDENT2 "%d: '%s', status=%s, outboundQueueLength=%u, " - "inputState.isNeutral=%s\n", - i, connection->getInputChannelName(), connection->getStatusLabel(), - connection->outboundQueue.count(), - toString(connection->inputState.isNeutral())); + if (!mInboundQueue.isEmpty()) { + dump.appendFormat(INDENT "InboundQueue: length=%u\n", mInboundQueue.count()); + for (EventEntry* entry = mInboundQueue.head; entry; entry = entry->next) { + dump.append(INDENT2); + entry->appendDescription(dump); + dump.appendFormat(", age=%0.1fms\n", + (currentTime - entry->eventTime) * 0.000001f); } } else { - dump.append(INDENT "ActiveConnections: \n"); + dump.append(INDENT "InboundQueue: \n"); + } + + if (!mConnectionsByFd.isEmpty()) { + dump.append(INDENT "Connections:\n"); + for (size_t i = 0; i < mConnectionsByFd.size(); i++) { + const sp& connection = mConnectionsByFd.valueAt(i); + dump.appendFormat(INDENT2 "%d: channelName='%s', windowName='%s', " + "status=%s, monitor=%s, inputPublisherBlocked=%s\n", + i, connection->getInputChannelName(), connection->getWindowName(), + connection->getStatusLabel(), toString(connection->monitor), + toString(connection->inputPublisherBlocked)); + + if (!connection->outboundQueue.isEmpty()) { + dump.appendFormat(INDENT3 "OutboundQueue: length=%u\n", + connection->outboundQueue.count()); + for (DispatchEntry* entry = connection->outboundQueue.head; entry; + entry = entry->next) { + dump.append(INDENT4); + entry->eventEntry->appendDescription(dump); + dump.appendFormat(", targetFlags=0x%08x, resolvedAction=%d, age=%0.1fms\n", + entry->targetFlags, entry->resolvedAction, + (currentTime - entry->eventEntry->eventTime) * 0.000001f); + } + } else { + dump.append(INDENT3 "OutboundQueue: \n"); + } + + if (!connection->waitQueue.isEmpty()) { + dump.appendFormat(INDENT3 "WaitQueue: length=%u\n", + connection->waitQueue.count()); + for (DispatchEntry* entry = connection->waitQueue.head; entry; + entry = entry->next) { + dump.append(INDENT4); + entry->eventEntry->appendDescription(dump); + dump.appendFormat(", targetFlags=0x%08x, resolvedAction=%d, " + "age=%0.1fms, wait=%0.1fms\n", + entry->targetFlags, entry->resolvedAction, + (currentTime - entry->eventEntry->eventTime) * 0.000001f, + (currentTime - entry->deliveryTime) * 0.000001f); + } + } else { + dump.append(INDENT3 "WaitQueue: \n"); + } + } + } else { + dump.append(INDENT "Connections: \n"); } if (isAppSwitchPendingLocked()) { - dump.appendFormat(INDENT "AppSwitch: pending, due in %01.1fms\n", + dump.appendFormat(INDENT "AppSwitch: pending, due in %0.1fms\n", (mAppSwitchDueTime - now()) / 1000000.0); } else { dump.append(INDENT "AppSwitch: not pending\n"); } + + dump.append(INDENT "Configuration:\n"); + dump.appendFormat(INDENT2 "KeyRepeatDelay: %0.1fms\n", + mConfig.keyRepeatDelay * 0.000001f); + dump.appendFormat(INDENT2 "KeyRepeatTimeout: %0.1fms\n", + mConfig.keyRepeatTimeout * 0.000001f); } status_t InputDispatcher::registerInputChannel(const sp& inputChannel, @@ -3652,24 +3250,19 @@ status_t InputDispatcher::registerInputChannel(const sp& inputChan } sp connection = new Connection(inputChannel, inputWindowHandle, monitor); - status_t status = connection->initialize(); - if (status) { - ALOGE("Failed to initialize input publisher for input channel '%s', status=%d", - inputChannel->getName().string(), status); - return status; - } - int32_t receiveFd = inputChannel->getReceivePipeFd(); - mConnectionsByReceiveFd.add(receiveFd, connection); + int fd = inputChannel->getFd(); + mConnectionsByFd.add(fd, connection); if (monitor) { mMonitoringChannels.push(inputChannel); } - mLooper->addFd(receiveFd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this); - - runCommandsLockedInterruptible(); + mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this); } // release lock + + // Wake the looper because some connections have changed. + mLooper->wake(); return OK; } @@ -3702,20 +3295,18 @@ status_t InputDispatcher::unregisterInputChannelLocked(const sp& i return BAD_VALUE; } - sp connection = mConnectionsByReceiveFd.valueAt(connectionIndex); - mConnectionsByReceiveFd.removeItemsAt(connectionIndex); + sp connection = mConnectionsByFd.valueAt(connectionIndex); + mConnectionsByFd.removeItemsAt(connectionIndex); if (connection->monitor) { removeMonitorChannelLocked(inputChannel); } - mLooper->removeFd(inputChannel->getReceivePipeFd()); + mLooper->removeFd(inputChannel->getFd()); nsecs_t currentTime = now(); abortBrokenDispatchCycleLocked(currentTime, connection, notify); - runCommandsLockedInterruptible(); - connection->status = Connection::STATUS_ZOMBIE; return OK; } @@ -3730,9 +3321,9 @@ void InputDispatcher::removeMonitorChannelLocked(const sp& inputCh } ssize_t InputDispatcher::getConnectionIndexLocked(const sp& inputChannel) { - ssize_t connectionIndex = mConnectionsByReceiveFd.indexOfKey(inputChannel->getReceivePipeFd()); + ssize_t connectionIndex = mConnectionsByFd.indexOfKey(inputChannel->getFd()); if (connectionIndex >= 0) { - sp connection = mConnectionsByReceiveFd.valueAt(connectionIndex); + sp connection = mConnectionsByFd.valueAt(connectionIndex); if (connection->inputChannel.get() == inputChannel.get()) { return connectionIndex; } @@ -3741,33 +3332,13 @@ ssize_t InputDispatcher::getConnectionIndexLocked(const sp& inputC return -1; } -void InputDispatcher::activateConnectionLocked(Connection* connection) { - for (size_t i = 0; i < mActiveConnections.size(); i++) { - if (mActiveConnections.itemAt(i) == connection) { - return; - } - } - mActiveConnections.add(connection); -} - -void InputDispatcher::deactivateConnectionLocked(Connection* connection) { - for (size_t i = 0; i < mActiveConnections.size(); i++) { - if (mActiveConnections.itemAt(i) == connection) { - mActiveConnections.removeAt(i); - return; - } - } -} - -void InputDispatcher::onDispatchCycleStartedLocked( - nsecs_t currentTime, const sp& connection) { -} - void InputDispatcher::onDispatchCycleFinishedLocked( - nsecs_t currentTime, const sp& connection, bool handled) { + nsecs_t currentTime, const sp& connection, uint32_t seq, bool handled) { CommandEntry* commandEntry = postCommandLocked( & InputDispatcher::doDispatchCycleFinishedLockedInterruptible); commandEntry->connection = connection; + commandEntry->eventTime = currentTime; + commandEntry->seq = seq; commandEntry->handled = handled; } @@ -3784,12 +3355,29 @@ void InputDispatcher::onDispatchCycleBrokenLocked( void InputDispatcher::onANRLocked( nsecs_t currentTime, const sp& applicationHandle, const sp& windowHandle, - nsecs_t eventTime, nsecs_t waitStartTime) { + nsecs_t eventTime, nsecs_t waitStartTime, const char* reason) { + float dispatchLatency = (currentTime - eventTime) * 0.000001f; + float waitDuration = (currentTime - waitStartTime) * 0.000001f; ALOGI("Application is not responding: %s. " - "%01.1fms since event, %01.1fms since wait started", + "It has been %0.1fms since event, %0.1fms since wait started. Reason: %s", getApplicationWindowLabelLocked(applicationHandle, windowHandle).string(), - (currentTime - eventTime) / 1000000.0, - (currentTime - waitStartTime) / 1000000.0); + dispatchLatency, waitDuration, reason); + + // Capture a record of the InputDispatcher state at the time of the ANR. + time_t t = time(NULL); + struct tm tm; + localtime_r(&t, &tm); + char timestr[64]; + strftime(timestr, sizeof(timestr), "%F %T", &tm); + mLastANRState.clear(); + mLastANRState.append(INDENT "ANR:\n"); + mLastANRState.appendFormat(INDENT2 "Time: %s\n", timestr); + mLastANRState.appendFormat(INDENT2 "Window: %s\n", + getApplicationWindowLabelLocked(applicationHandle, windowHandle).string()); + mLastANRState.appendFormat(INDENT2 "DispatchLatency: %0.1fms\n", dispatchLatency); + mLastANRState.appendFormat(INDENT2 "WaitDuration: %0.1fms\n", waitDuration); + mLastANRState.appendFormat(INDENT2 "Reason: %s\n", reason); + dumpDispatchStateLocked(mLastANRState); CommandEntry* commandEntry = postCommandLocked( & InputDispatcher::doNotifyANRLockedInterruptible); @@ -3861,26 +3449,52 @@ void InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible( void InputDispatcher::doDispatchCycleFinishedLockedInterruptible( CommandEntry* commandEntry) { sp connection = commandEntry->connection; + nsecs_t finishTime = commandEntry->eventTime; + uint32_t seq = commandEntry->seq; bool handled = commandEntry->handled; - bool skipNext = false; - if (!connection->outboundQueue.isEmpty()) { - DispatchEntry* dispatchEntry = connection->outboundQueue.head; - if (dispatchEntry->inProgress) { - if (dispatchEntry->eventEntry->type == EventEntry::TYPE_KEY) { - KeyEntry* keyEntry = static_cast(dispatchEntry->eventEntry); - skipNext = afterKeyEventLockedInterruptible(connection, - dispatchEntry, keyEntry, handled); - } else if (dispatchEntry->eventEntry->type == EventEntry::TYPE_MOTION) { - MotionEntry* motionEntry = static_cast(dispatchEntry->eventEntry); - skipNext = afterMotionEventLockedInterruptible(connection, - dispatchEntry, motionEntry, handled); + // Handle post-event policy actions. + DispatchEntry* dispatchEntry = connection->findWaitQueueEntry(seq); + if (dispatchEntry) { + nsecs_t eventDuration = finishTime - dispatchEntry->deliveryTime; + if (eventDuration > SLOW_EVENT_PROCESSING_WARNING_TIMEOUT) { + String8 msg; + msg.appendFormat("Window '%s' spent %0.1fms processing the last input event: ", + connection->getWindowName(), eventDuration * 0.000001f); + dispatchEntry->eventEntry->appendDescription(msg); + ALOGI("%s", msg.string()); + } + + bool restartEvent; + if (dispatchEntry->eventEntry->type == EventEntry::TYPE_KEY) { + KeyEntry* keyEntry = static_cast(dispatchEntry->eventEntry); + restartEvent = afterKeyEventLockedInterruptible(connection, + dispatchEntry, keyEntry, handled); + } else if (dispatchEntry->eventEntry->type == EventEntry::TYPE_MOTION) { + MotionEntry* motionEntry = static_cast(dispatchEntry->eventEntry); + restartEvent = afterMotionEventLockedInterruptible(connection, + dispatchEntry, motionEntry, handled); + } else { + restartEvent = false; + } + + // Dequeue the event and start the next cycle. + // Note that because the lock might have been released, it is possible that the + // contents of the wait queue to have been drained, so we need to double-check + // a few things. + if (dispatchEntry == connection->findWaitQueueEntry(seq)) { + connection->waitQueue.dequeue(dispatchEntry); + traceWaitQueueLengthLocked(connection); + if (restartEvent && connection->status == Connection::STATUS_NORMAL) { + connection->outboundQueue.enqueueAtHead(dispatchEntry); + traceOutboundQueueLengthLocked(connection); + } else { + releaseDispatchEntryLocked(dispatchEntry); } } - } - if (!skipNext) { - startNextDispatchCycleLocked(now(), connection); + // Start the next dispatch cycle for this connection. + startDispatchCycleLocked(now(), connection); } } @@ -3900,6 +3514,25 @@ bool InputDispatcher::afterKeyEventLockedInterruptible(const sp& con // generated a fallback or if the window is not a foreground window, // then cancel the associated fallback key, if any. if (fallbackKeyCode != -1) { + // Dispatch the unhandled key to the policy with the cancel flag. +#if DEBUG_OUTBOUND_EVENT_DETAILS + ALOGD("Unhandled key event: Asking policy to cancel fallback action. " + "keyCode=%d, action=%d, repeatCount=%d, policyFlags=0x%08x", + keyEntry->keyCode, keyEntry->action, keyEntry->repeatCount, + keyEntry->policyFlags); +#endif + KeyEvent event; + initializeKeyEvent(&event, keyEntry); + event.setFlags(event.getFlags() | AKEY_EVENT_FLAG_CANCELED); + + mLock.unlock(); + + mPolicy->dispatchUnhandledKey(connection->inputWindowHandle, + &event, keyEntry->policyFlags, &event); + + mLock.lock(); + + // Cancel the fallback key. if (fallbackKeyCode != AKEYCODE_UNKNOWN) { CancelationOptions options(CancelationOptions::CANCEL_FALLBACK_EVENTS, "application handled the original non-fallback key " @@ -3920,8 +3553,9 @@ bool InputDispatcher::afterKeyEventLockedInterruptible(const sp& con #if DEBUG_OUTBOUND_EVENT_DETAILS ALOGD("Unhandled key event: Skipping unhandled key event processing " "since this is not an initial down. " - "keyCode=%d, action=%d, repeatCount=%d", - originalKeyCode, keyEntry->action, keyEntry->repeatCount); + "keyCode=%d, action=%d, repeatCount=%d, policyFlags=0x%08x", + originalKeyCode, keyEntry->action, keyEntry->repeatCount, + keyEntry->policyFlags); #endif return false; } @@ -3929,8 +3563,9 @@ bool InputDispatcher::afterKeyEventLockedInterruptible(const sp& con // Dispatch the unhandled key to the policy. #if DEBUG_OUTBOUND_EVENT_DETAILS ALOGD("Unhandled key event: Asking policy to perform fallback action. " - "keyCode=%d, action=%d, repeatCount=%d", - keyEntry->keyCode, keyEntry->action, keyEntry->repeatCount); + "keyCode=%d, action=%d, repeatCount=%d, policyFlags=0x%08x", + keyEntry->keyCode, keyEntry->action, keyEntry->repeatCount, + keyEntry->policyFlags); #endif KeyEvent event; initializeKeyEvent(&event, keyEntry); @@ -3944,11 +3579,9 @@ bool InputDispatcher::afterKeyEventLockedInterruptible(const sp& con if (connection->status != Connection::STATUS_NORMAL) { connection->inputState.removeFallbackKey(originalKeyCode); - return true; // skip next cycle + return false; } - ALOG_ASSERT(connection->outboundQueue.head == dispatchEntry); - // Latch the fallback keycode for this key on an initial down. // The fallback keycode cannot change at any other point in the lifecycle. if (initialDown) { @@ -3974,7 +3607,7 @@ bool InputDispatcher::afterKeyEventLockedInterruptible(const sp& con "to send %d instead. Fallback canceled.", event.getKeyCode(), originalKeyCode, fallbackKeyCode); } else { - ALOGD("Unhandled key event: Policy did not request fallback for %d," + ALOGD("Unhandled key event: Policy did not request fallback for %d, " "but on the DOWN it had requested to send %d. " "Fallback canceled.", originalKeyCode, fallbackKeyCode); @@ -4026,10 +3659,7 @@ bool InputDispatcher::afterKeyEventLockedInterruptible(const sp& con "originalKeyCode=%d, fallbackKeyCode=%d, fallbackMetaState=%08x", originalKeyCode, fallbackKeyCode, keyEntry->metaState); #endif - - dispatchEntry->inProgress = false; - startDispatchCycleLocked(now(), connection); - return true; // already started next cycle + return true; // restart the event } else { #if DEBUG_OUTBOUND_EVENT_DETAILS ALOGD("Unhandled key event: No fallback key."); @@ -4064,21 +3694,51 @@ void InputDispatcher::updateDispatchStatisticsLocked(nsecs_t currentTime, const // TODO Write some statistics about how long we spend waiting. } +void InputDispatcher::traceInboundQueueLengthLocked() { +#ifdef HAVE_ANDROID_OS + if (ATRACE_ENABLED()) { + ATRACE_INT("iq", mInboundQueue.count()); + } +#endif +} + +void InputDispatcher::traceOutboundQueueLengthLocked(const sp& connection) { +#ifdef HAVE_ANDROID_OS + if (ATRACE_ENABLED()) { + char counterName[40]; + snprintf(counterName, sizeof(counterName), "oq:%s", connection->getWindowName()); + ATRACE_INT(counterName, connection->outboundQueue.count()); + } +#endif +} + +void InputDispatcher::traceWaitQueueLengthLocked(const sp& connection) { +#ifdef HAVE_ANDROID_OS + if (ATRACE_ENABLED()) { + char counterName[40]; + snprintf(counterName, sizeof(counterName), "wq:%s", connection->getWindowName()); + ATRACE_INT(counterName, connection->waitQueue.count()); + } +#endif +} + void InputDispatcher::dump(String8& dump) { AutoMutex _l(mLock); dump.append("Input Dispatcher State:\n"); dumpDispatchStateLocked(dump); - dump.append(INDENT "Configuration:\n"); - dump.appendFormat(INDENT2 "MaxEventsPerSecond: %d\n", mConfig.maxEventsPerSecond); - dump.appendFormat(INDENT2 "KeyRepeatDelay: %0.1fms\n", mConfig.keyRepeatDelay * 0.000001f); - dump.appendFormat(INDENT2 "KeyRepeatTimeout: %0.1fms\n", mConfig.keyRepeatTimeout * 0.000001f); + if (!mLastANRState.isEmpty()) { + dump.append("\nInput Dispatcher State at time of last ANR:\n"); + dump.append(mLastANRState); + } } void InputDispatcher::monitor() { // Acquire and release the lock to ensure that the dispatcher has not deadlocked. mLock.lock(); + mLooper->wake(); + mDispatcherIsAliveCondition.wait(mLock); mLock.unlock(); } @@ -4154,6 +3814,10 @@ InputDispatcher::ConfigurationChangedEntry::ConfigurationChangedEntry(nsecs_t ev InputDispatcher::ConfigurationChangedEntry::~ConfigurationChangedEntry() { } +void InputDispatcher::ConfigurationChangedEntry::appendDescription(String8& msg) const { + msg.append("ConfigurationChangedEvent()"); +} + // --- InputDispatcher::DeviceResetEntry --- @@ -4165,6 +3829,10 @@ InputDispatcher::DeviceResetEntry::DeviceResetEntry(nsecs_t eventTime, int32_t d InputDispatcher::DeviceResetEntry::~DeviceResetEntry() { } +void InputDispatcher::DeviceResetEntry::appendDescription(String8& msg) const { + msg.appendFormat("DeviceResetEvent(deviceId=%d)", deviceId); +} + // --- InputDispatcher::KeyEntry --- @@ -4183,6 +3851,11 @@ InputDispatcher::KeyEntry::KeyEntry(nsecs_t eventTime, InputDispatcher::KeyEntry::~KeyEntry() { } +void InputDispatcher::KeyEntry::appendDescription(String8& msg) const { + msg.appendFormat("KeyEvent(action=%d, deviceId=%d, source=0x%08x)", + action, deviceId, source); +} + void InputDispatcher::KeyEntry::recycle() { releaseInjectionState(); @@ -4193,86 +3866,45 @@ void InputDispatcher::KeyEntry::recycle() { } -// --- InputDispatcher::MotionSample --- - -InputDispatcher::MotionSample::MotionSample(nsecs_t eventTime, - const PointerCoords* pointerCoords, uint32_t pointerCount) : - next(NULL), eventTime(eventTime), eventTimeBeforeCoalescing(eventTime) { - for (uint32_t i = 0; i < pointerCount; i++) { - this->pointerCoords[i].copyFrom(pointerCoords[i]); - } -} - - // --- InputDispatcher::MotionEntry --- InputDispatcher::MotionEntry::MotionEntry(nsecs_t eventTime, int32_t deviceId, uint32_t source, uint32_t policyFlags, int32_t action, int32_t flags, int32_t metaState, int32_t buttonState, int32_t edgeFlags, float xPrecision, float yPrecision, - nsecs_t downTime, uint32_t pointerCount, + nsecs_t downTime, int32_t displayId, uint32_t pointerCount, const PointerProperties* pointerProperties, const PointerCoords* pointerCoords) : EventEntry(TYPE_MOTION, eventTime, policyFlags), + eventTime(eventTime), deviceId(deviceId), source(source), action(action), flags(flags), metaState(metaState), buttonState(buttonState), edgeFlags(edgeFlags), xPrecision(xPrecision), yPrecision(yPrecision), - downTime(downTime), pointerCount(pointerCount), - firstSample(eventTime, pointerCoords, pointerCount), - lastSample(&firstSample) { + downTime(downTime), displayId(displayId), pointerCount(pointerCount) { for (uint32_t i = 0; i < pointerCount; i++) { this->pointerProperties[i].copyFrom(pointerProperties[i]); + this->pointerCoords[i].copyFrom(pointerCoords[i]); } } InputDispatcher::MotionEntry::~MotionEntry() { - for (MotionSample* sample = firstSample.next; sample != NULL; ) { - MotionSample* next = sample->next; - delete sample; - sample = next; - } } -uint32_t InputDispatcher::MotionEntry::countSamples() const { - uint32_t count = 1; - for (MotionSample* sample = firstSample.next; sample != NULL; sample = sample->next) { - count += 1; - } - return count; -} - -bool InputDispatcher::MotionEntry::canAppendSamples(int32_t action, uint32_t pointerCount, - const PointerProperties* pointerProperties) const { - if (this->action != action - || this->pointerCount != pointerCount - || this->isInjected()) { - return false; - } - for (uint32_t i = 0; i < pointerCount; i++) { - if (this->pointerProperties[i] != pointerProperties[i]) { - return false; - } - } - return true; -} - -void InputDispatcher::MotionEntry::appendSample( - nsecs_t eventTime, const PointerCoords* pointerCoords) { - MotionSample* sample = new MotionSample(eventTime, pointerCoords, pointerCount); - - lastSample->next = sample; - lastSample = sample; +void InputDispatcher::MotionEntry::appendDescription(String8& msg) const { + msg.appendFormat("MotionEvent(action=%d, deviceId=%d, source=0x%08x, displayId=%d)", + action, deviceId, source, displayId); } // --- InputDispatcher::DispatchEntry --- +volatile int32_t InputDispatcher::DispatchEntry::sNextSeqAtomic; + InputDispatcher::DispatchEntry::DispatchEntry(EventEntry* eventEntry, int32_t targetFlags, float xOffset, float yOffset, float scaleFactor) : + seq(nextSeq()), eventEntry(eventEntry), targetFlags(targetFlags), xOffset(xOffset), yOffset(yOffset), scaleFactor(scaleFactor), - inProgress(false), - resolvedAction(0), resolvedFlags(0), - headMotionSample(NULL), tailMotionSample(NULL) { + deliveryTime(0), resolvedAction(0), resolvedFlags(0) { eventEntry->refCount += 1; } @@ -4280,6 +3912,15 @@ InputDispatcher::DispatchEntry::~DispatchEntry() { eventEntry->release(); } +uint32_t InputDispatcher::DispatchEntry::nextSeq() { + // Sequence number 0 is reserved and will never be returned. + uint32_t seq; + do { + seq = android_atomic_inc(&sNextSeqAtomic); + } while (!seq); + return seq; +} + // --- InputDispatcher::InputState --- @@ -4293,11 +3934,13 @@ bool InputDispatcher::InputState::isNeutral() const { return mKeyMementos.isEmpty() && mMotionMementos.isEmpty(); } -bool InputDispatcher::InputState::isHovering(int32_t deviceId, uint32_t source) const { +bool InputDispatcher::InputState::isHovering(int32_t deviceId, uint32_t source, + int32_t displayId) const { for (size_t i = 0; i < mMotionMementos.size(); i++) { const MotionMemento& memento = mMotionMementos.itemAt(i); if (memento.deviceId == deviceId && memento.source == source + && memento.displayId == displayId && memento.hovering) { return true; } @@ -4454,6 +4097,7 @@ ssize_t InputDispatcher::InputState::findMotionMemento(const MotionEntry* entry, const MotionMemento& memento = mMotionMementos.itemAt(i); if (memento.deviceId == entry->deviceId && memento.source == entry->source + && memento.displayId == entry->displayId && memento.hovering == hovering) { return i; } @@ -4468,8 +4112,10 @@ void InputDispatcher::InputState::addKeyMemento(const KeyEntry* entry, int32_t f memento.source = entry->source; memento.keyCode = entry->keyCode; memento.scanCode = entry->scanCode; + memento.metaState = entry->metaState; memento.flags = flags; memento.downTime = entry->downTime; + memento.policyFlags = entry->policyFlags; } void InputDispatcher::InputState::addMotionMemento(const MotionEntry* entry, @@ -4482,15 +4128,17 @@ void InputDispatcher::InputState::addMotionMemento(const MotionEntry* entry, memento.xPrecision = entry->xPrecision; memento.yPrecision = entry->yPrecision; memento.downTime = entry->downTime; + memento.displayId = entry->displayId; memento.setPointers(entry); memento.hovering = hovering; + memento.policyFlags = entry->policyFlags; } void InputDispatcher::InputState::MotionMemento::setPointers(const MotionEntry* entry) { pointerCount = entry->pointerCount; for (uint32_t i = 0; i < entry->pointerCount; i++) { pointerProperties[i].copyFrom(entry->pointerProperties[i]); - pointerCoords[i].copyFrom(entry->lastSample->pointerCoords[i]); + pointerCoords[i].copyFrom(entry->pointerCoords[i]); } } @@ -4500,9 +4148,9 @@ void InputDispatcher::InputState::synthesizeCancelationEvents(nsecs_t currentTim const KeyMemento& memento = mKeyMementos.itemAt(i); if (shouldCancelKey(memento, options)) { outEvents.push(new KeyEntry(currentTime, - memento.deviceId, memento.source, 0, + memento.deviceId, memento.source, memento.policyFlags, AKEY_EVENT_ACTION_UP, memento.flags | AKEY_EVENT_FLAG_CANCELED, - memento.keyCode, memento.scanCode, 0, 0, memento.downTime)); + memento.keyCode, memento.scanCode, memento.metaState, 0, memento.downTime)); } } @@ -4510,12 +4158,13 @@ void InputDispatcher::InputState::synthesizeCancelationEvents(nsecs_t currentTim const MotionMemento& memento = mMotionMementos.itemAt(i); if (shouldCancelMotion(memento, options)) { outEvents.push(new MotionEntry(currentTime, - memento.deviceId, memento.source, 0, + memento.deviceId, memento.source, memento.policyFlags, memento.hovering ? AMOTION_EVENT_ACTION_HOVER_EXIT : AMOTION_EVENT_ACTION_CANCEL, memento.flags, 0, 0, 0, memento.xPrecision, memento.yPrecision, memento.downTime, + memento.displayId, memento.pointerCount, memento.pointerProperties, memento.pointerCoords)); } } @@ -4534,7 +4183,8 @@ void InputDispatcher::InputState::copyPointerStateTo(InputState& other) const { for (size_t j = 0; j < other.mMotionMementos.size(); ) { const MotionMemento& otherMemento = other.mMotionMementos.itemAt(j); if (memento.deviceId == otherMemento.deviceId - && memento.source == otherMemento.source) { + && memento.source == otherMemento.source + && memento.displayId == otherMemento.displayId) { other.mMotionMementos.removeAt(j); } else { j += 1; @@ -4610,15 +4260,20 @@ InputDispatcher::Connection::Connection(const sp& inputChannel, const sp& inputWindowHandle, bool monitor) : status(STATUS_NORMAL), inputChannel(inputChannel), inputWindowHandle(inputWindowHandle), monitor(monitor), - inputPublisher(inputChannel), - lastEventTime(LONG_LONG_MAX), lastDispatchTime(LONG_LONG_MAX) { + inputPublisher(inputChannel), inputPublisherBlocked(false) { } InputDispatcher::Connection::~Connection() { } -status_t InputDispatcher::Connection::initialize() { - return inputPublisher.initialize(); +const char* InputDispatcher::Connection::getWindowName() const { + if (inputWindowHandle != NULL) { + return inputWindowHandle->getName().string(); + } + if (monitor) { + return "monitor"; + } + return "?"; } const char* InputDispatcher::Connection::getStatusLabel() const { @@ -4637,12 +4292,10 @@ const char* InputDispatcher::Connection::getStatusLabel() const { } } -InputDispatcher::DispatchEntry* InputDispatcher::Connection::findQueuedDispatchEntryForEvent( - const EventEntry* eventEntry) const { - for (DispatchEntry* dispatchEntry = outboundQueue.tail; dispatchEntry; - dispatchEntry = dispatchEntry->prev) { - if (dispatchEntry->eventEntry == eventEntry) { - return dispatchEntry; +InputDispatcher::DispatchEntry* InputDispatcher::Connection::findWaitQueueEntry(uint32_t seq) { + for (DispatchEntry* entry = waitQueue.head; entry != NULL; entry = entry->next) { + if (entry->seq == seq) { + return entry; } } return NULL; @@ -4652,7 +4305,8 @@ InputDispatcher::DispatchEntry* InputDispatcher::Connection::findQueuedDispatchE // --- InputDispatcher::CommandEntry --- InputDispatcher::CommandEntry::CommandEntry(Command command) : - command(command), eventTime(0), keyEntry(NULL), userActivityEventType(0), handled(false) { + command(command), eventTime(0), keyEntry(NULL), userActivityEventType(0), + seq(0), handled(false) { } InputDispatcher::CommandEntry::~CommandEntry() { @@ -4662,7 +4316,7 @@ InputDispatcher::CommandEntry::~CommandEntry() { // --- InputDispatcher::TouchState --- InputDispatcher::TouchState::TouchState() : - down(false), split(false), deviceId(-1), source(0) { + down(false), split(false), deviceId(-1), source(0), displayId(-1) { } InputDispatcher::TouchState::~TouchState() { @@ -4673,6 +4327,7 @@ void InputDispatcher::TouchState::reset() { split = false; deviceId = -1; source = 0; + displayId = -1; windows.clear(); } @@ -4681,6 +4336,7 @@ void InputDispatcher::TouchState::copyFrom(const TouchState& other) { split = other.split; deviceId = other.deviceId; source = other.source; + displayId = other.displayId; windows = other.windows; } @@ -4710,6 +4366,15 @@ void InputDispatcher::TouchState::addOrUpdateWindow(const sp& touchedWindow.pointerIds = pointerIds; } +void InputDispatcher::TouchState::removeWindow(const sp& windowHandle) { + for (size_t i = 0; i < windows.size(); i++) { + if (windows.itemAt(i).windowHandle == windowHandle) { + windows.removeAt(i); + return; + } + } +} + void InputDispatcher::TouchState::filterNonAsIsTouchWindows() { for (size_t i = 0 ; i < windows.size(); ) { TouchedWindow& window = windows.editItemAt(i); diff --git a/widget/gonk/libui/InputDispatcher.h b/widget/gonk/libui/InputDispatcher.h index eb0f413b5eb9..5453421f62af 100644 --- a/widget/gonk/libui/InputDispatcher.h +++ b/widget/gonk/libui/InputDispatcher.h @@ -17,17 +17,17 @@ #ifndef _UI_INPUT_DISPATCHER_H #define _UI_INPUT_DISPATCHER_H -#include "cutils_log.h" #include "Input.h" #include "InputTransport.h" #include #include #include -#include "Timers.h" +#include #include -#include "String8.h" +#include #include -#include "BitSet.h" +#include +#include #include #include @@ -165,6 +165,8 @@ struct InputTarget { * Input dispatcher configuration. * * Specifies various options that modify the behavior of the input dispatcher. + * The values provided here are merely defaults. The actual values will come from ViewConfiguration + * and are passed into the dispatcher during initialization. */ struct InputDispatcherConfiguration { // The key repeat initial timeout. @@ -173,15 +175,9 @@ struct InputDispatcherConfiguration { // The key repeat inter-key delay. nsecs_t keyRepeatDelay; - // The maximum suggested event delivery rate per second. - // This value is used to throttle motion event movement actions on a per-device - // basis. It is not intended to be a hard limit. - int32_t maxEventsPerSecond; - InputDispatcherConfiguration() : keyRepeatTimeout(500 * 1000000LL), - keyRepeatDelay(50 * 1000000LL), - maxEventsPerSecond(60) { } + keyRepeatDelay(50 * 1000000LL) { } }; @@ -254,7 +250,7 @@ public: /* Notifies the policy about switch events. */ virtual void notifySwitch(nsecs_t when, - int32_t switchCode, int32_t switchValue, uint32_t policyFlags) = 0; + uint32_t switchValues, uint32_t switchMask, uint32_t policyFlags) = 0; /* Poke user activity for an event dispatched to a window. */ virtual void pokeUserActivity(nsecs_t eventTime, int32_t eventType) = 0; @@ -405,6 +401,9 @@ private: struct Link { T* next; T* prev; + + protected: + inline Link() : next(NULL), prev(NULL) { } }; struct InjectionState { @@ -443,6 +442,8 @@ private: void release(); + virtual void appendDescription(String8& msg) const = 0; + protected: EventEntry(int32_t type, nsecs_t eventTime, uint32_t policyFlags); virtual ~EventEntry(); @@ -451,6 +452,7 @@ private: struct ConfigurationChangedEntry : EventEntry { ConfigurationChangedEntry(nsecs_t eventTime); + virtual void appendDescription(String8& msg) const; protected: virtual ~ConfigurationChangedEntry(); @@ -460,6 +462,7 @@ private: int32_t deviceId; DeviceResetEntry(nsecs_t eventTime, int32_t deviceId); + virtual void appendDescription(String8& msg) const; protected: virtual ~DeviceResetEntry(); @@ -491,24 +494,15 @@ private: int32_t deviceId, uint32_t source, uint32_t policyFlags, int32_t action, int32_t flags, int32_t keyCode, int32_t scanCode, int32_t metaState, int32_t repeatCount, nsecs_t downTime); + virtual void appendDescription(String8& msg) const; void recycle(); protected: virtual ~KeyEntry(); }; - struct MotionSample { - MotionSample* next; - - nsecs_t eventTime; // may be updated during coalescing - nsecs_t eventTimeBeforeCoalescing; // not updated during coalescing - PointerCoords pointerCoords[MAX_POINTERS]; - - MotionSample(nsecs_t eventTime, const PointerCoords* pointerCoords, - uint32_t pointerCount); - }; - struct MotionEntry : EventEntry { + nsecs_t eventTime; int32_t deviceId; uint32_t source; int32_t action; @@ -519,27 +513,19 @@ private: float xPrecision; float yPrecision; nsecs_t downTime; + int32_t displayId; uint32_t pointerCount; PointerProperties pointerProperties[MAX_POINTERS]; - - // Linked list of motion samples associated with this motion event. - MotionSample firstSample; - MotionSample* lastSample; + PointerCoords pointerCoords[MAX_POINTERS]; MotionEntry(nsecs_t eventTime, - int32_t deviceId, uint32_t source, uint32_t policyFlags, int32_t action, - int32_t flags, int32_t metaState, int32_t buttonState, int32_t edgeFlags, + int32_t deviceId, uint32_t source, uint32_t policyFlags, + int32_t action, int32_t flags, + int32_t metaState, int32_t buttonState, int32_t edgeFlags, float xPrecision, float yPrecision, - nsecs_t downTime, uint32_t pointerCount, + nsecs_t downTime, int32_t displayId, uint32_t pointerCount, const PointerProperties* pointerProperties, const PointerCoords* pointerCoords); - - uint32_t countSamples() const; - - // Checks whether we can append samples, assuming the device id and source are the same. - bool canAppendSamples(int32_t action, uint32_t pointerCount, - const PointerProperties* pointerProperties) const; - - void appendSample(nsecs_t eventTime, const PointerCoords* pointerCoords); + virtual void appendDescription(String8& msg) const; protected: virtual ~MotionEntry(); @@ -547,32 +533,19 @@ private: // Tracks the progress of dispatching a particular event to a particular connection. struct DispatchEntry : Link { + const uint32_t seq; // unique sequence number, never 0 + EventEntry* eventEntry; // the event to dispatch int32_t targetFlags; float xOffset; float yOffset; float scaleFactor; - - // True if dispatch has started. - bool inProgress; + nsecs_t deliveryTime; // time when the event was actually delivered // Set to the resolved action and flags when the event is enqueued. int32_t resolvedAction; int32_t resolvedFlags; - // For motion events: - // Pointer to the first motion sample to dispatch in this cycle. - // Usually NULL to indicate that the list of motion samples begins at - // MotionEntry::firstSample. Otherwise, some samples were dispatched in a previous - // cycle and this pointer indicates the location of the first remainining sample - // to dispatch during the current cycle. - MotionSample* headMotionSample; - // Pointer to a motion sample to dispatch in the next cycle if the dispatcher was - // unable to send all motion samples during this cycle. On the next cycle, - // headMotionSample will be initialized to tailMotionSample and tailMotionSample - // will be set to NULL. - MotionSample* tailMotionSample; - DispatchEntry(EventEntry* eventEntry, int32_t targetFlags, float xOffset, float yOffset, float scaleFactor); ~DispatchEntry(); @@ -584,6 +557,11 @@ private: inline bool isSplit() const { return targetFlags & InputTarget::FLAG_SPLIT; } + + private: + static volatile int32_t sNextSeqAtomic; + + static uint32_t nextSeq(); }; // A command entry captures state and behavior for an action to be performed in the @@ -619,6 +597,7 @@ private: sp inputApplicationHandle; sp inputWindowHandle; int32_t userActivityEventType; + uint32_t seq; bool handled; }; @@ -721,7 +700,7 @@ private: // Returns true if the specified source is known to have received a hover enter // motion event. - bool isHovering(int32_t deviceId, uint32_t source) const; + bool isHovering(int32_t deviceId, uint32_t source, int32_t displayId) const; // Records tracking information for a key event that has just been published. // Returns true if the event should be delivered, false if it is inconsistent @@ -764,8 +743,10 @@ private: uint32_t source; int32_t keyCode; int32_t scanCode; + int32_t metaState; int32_t flags; nsecs_t downTime; + uint32_t policyFlags; }; struct MotionMemento { @@ -775,10 +756,12 @@ private: float xPrecision; float yPrecision; nsecs_t downTime; + int32_t displayId; uint32_t pointerCount; PointerProperties pointerProperties[MAX_POINTERS]; PointerCoords pointerCoords[MAX_POINTERS]; bool hovering; + uint32_t policyFlags; void setPointers(const MotionEntry* entry); }; @@ -820,33 +803,27 @@ private: bool monitor; InputPublisher inputPublisher; InputState inputState; + + // True if the socket is full and no further events can be published until + // the application consumes some of the input. + bool inputPublisherBlocked; + + // Queue of events that need to be published to the connection. Queue outboundQueue; - nsecs_t lastEventTime; // the time when the event was originally captured - nsecs_t lastDispatchTime; // the time when the last event was dispatched + // Queue of events that have been published to the connection but that have not + // yet received a "finished" response from the application. + Queue waitQueue; explicit Connection(const sp& inputChannel, const sp& inputWindowHandle, bool monitor); inline const char* getInputChannelName() const { return inputChannel->getName().string(); } + const char* getWindowName() const; const char* getStatusLabel() const; - // Finds a DispatchEntry in the outbound queue associated with the specified event. - // Returns NULL if not found. - DispatchEntry* findQueuedDispatchEntryForEvent(const EventEntry* eventEntry) const; - - // Gets the time since the current event was originally obtained from the input driver. - inline double getEventLatencyMillis(nsecs_t currentTime) const { - return (currentTime - lastEventTime) / 1000000.0; - } - - // Gets the time since the current event entered the outbound dispatch queue. - inline double getDispatchLatencyMillis(nsecs_t currentTime) const { - return (currentTime - lastDispatchTime) / 1000000.0; - } - - status_t initialize(); + DispatchEntry* findWaitQueueEntry(uint32_t seq); }; enum DropReason { @@ -863,21 +840,15 @@ private: Mutex mLock; + Condition mDispatcherIsAliveCondition; + sp mLooper; EventEntry* mPendingEvent; Queue mInboundQueue; Queue mCommandQueue; - Vector mTempCancelationEvents; - void dispatchOnceInnerLocked(nsecs_t* nextWakeupTime); - void dispatchIdleLocked(); - - // Batches a new sample onto a motion entry. - // Assumes that the we have already checked that we can append samples. - void batchMotionLocked(MotionEntry* entry, nsecs_t eventTime, int32_t metaState, - const PointerCoords* pointerCoords, const char* eventDescription); // Enqueues an inbound event. Returns true if mLooper->wake() should be called. bool enqueueInboundEventLocked(EventEntry* entry); @@ -901,19 +872,13 @@ private: // to transfer focus to a new application. EventEntry* mNextUnblockedEvent; - sp findTouchedWindowAtLocked(int32_t x, int32_t y); + sp findTouchedWindowAtLocked(int32_t displayId, int32_t x, int32_t y); - // All registered connections mapped by receive pipe file descriptor. - KeyedVector > mConnectionsByReceiveFd; + // All registered connections mapped by channel file descriptor. + KeyedVector > mConnectionsByFd; ssize_t getConnectionIndexLocked(const sp& inputChannel); - // Active connections are connections that have a non-empty outbound queue. - // We don't use a ref-counted pointer here because we explicitly abort connections - // during unregistration which causes the connection's outbound queue to be cleared - // and the connection itself to be deactivated. - Vector mActiveConnections; - // Input channels that will receive a copy of all input events. Vector > mMonitoringChannels; @@ -926,17 +891,6 @@ private: void incrementPendingForegroundDispatchesLocked(EventEntry* entry); void decrementPendingForegroundDispatchesLocked(EventEntry* entry); - // Throttling state. - struct ThrottleState { - nsecs_t minTimeBetweenEvents; - - nsecs_t lastEventTime; - int32_t lastDeviceId; - uint32_t lastSource; - - uint32_t originalSampleCount; // only collected during debugging - } mThrottleState; - // Key repeat tracking. struct KeyRepeatState { KeyEntry* lastKeyEntry; // or null if no repeat @@ -947,9 +901,14 @@ private: KeyEntry* synthesizeKeyRepeatLocked(nsecs_t currentTime); // Deferred command processing. + bool haveCommandsLocked() const; bool runCommandsLockedInterruptible(); CommandEntry* postCommandLocked(Command command); + // Input filter processing. + bool shouldSendKeyToInputFilterLocked(const NotifyKeyArgs* args); + bool shouldSendMotionToInputFilterLocked(const NotifyMotionArgs* args); + // Inbound event processing. void drainInboundQueueLocked(); void releasePendingEventLocked(); @@ -979,6 +938,7 @@ private: bool split; int32_t deviceId; // id of the device that is currently down, others are rejected uint32_t source; // source of the device that is current down, others are rejected + int32_t displayId; // id to the display that currently has a touch, others are rejected Vector windows; TouchState(); @@ -987,6 +947,7 @@ private: void copyFrom(const TouchState& other); void addOrUpdateWindow(const sp& windowHandle, int32_t targetFlags, BitSet32 pointerIds); + void removeWindow(const sp& windowHandle); void filterNonAsIsTouchWindows(); sp getFirstForegroundWindowHandle() const; bool isSlippery() const; @@ -998,6 +959,9 @@ private: // Focused application. sp mFocusedApplicationHandle; + // Dispatcher state at time of last ANR. + String8 mLastANRState; + // Dispatch inbound events. bool dispatchConfigurationChangedLocked( nsecs_t currentTime, ConfigurationChangedEntry* entry); @@ -1009,16 +973,13 @@ private: bool dispatchMotionLocked( nsecs_t currentTime, MotionEntry* entry, DropReason* dropReason, nsecs_t* nextWakeupTime); - void dispatchEventToCurrentInputTargetsLocked( - nsecs_t currentTime, EventEntry* entry, bool resumeWithAppendedMotionSample); + void dispatchEventLocked(nsecs_t currentTime, EventEntry* entry, + const Vector& inputTargets); void logOutboundKeyDetailsLocked(const char* prefix, const KeyEntry* entry); void logOutboundMotionDetailsLocked(const char* prefix, const MotionEntry* entry); - // The input targets that were most recently identified for dispatch. - bool mCurrentInputTargetsValid; // false while targets are being recomputed - Vector mCurrentInputTargets; - + // Keeping track of ANR timeouts. enum InputTargetWaitCause { INPUT_TARGET_WAIT_CAUSE_NONE, INPUT_TARGET_WAIT_CAUSE_SYSTEM_NOT_READY, @@ -1035,32 +996,32 @@ private: sp mLastHoverWindowHandle; // Finding targets for input events. - void resetTargetsLocked(); - void commitTargetsLocked(); int32_t handleTargetsNotReadyLocked(nsecs_t currentTime, const EventEntry* entry, const sp& applicationHandle, const sp& windowHandle, - nsecs_t* nextWakeupTime); + nsecs_t* nextWakeupTime, const char* reason); void resumeAfterTargetsNotReadyTimeoutLocked(nsecs_t newTimeout, const sp& inputChannel); nsecs_t getTimeSpentWaitingForApplicationLocked(nsecs_t currentTime); void resetANRTimeoutsLocked(); int32_t findFocusedWindowTargetsLocked(nsecs_t currentTime, const EventEntry* entry, - nsecs_t* nextWakeupTime); + Vector& inputTargets, nsecs_t* nextWakeupTime); int32_t findTouchedWindowTargetsLocked(nsecs_t currentTime, const MotionEntry* entry, - nsecs_t* nextWakeupTime, bool* outConflictingPointerActions, - const MotionSample** outSplitBatchAfterSample); + Vector& inputTargets, nsecs_t* nextWakeupTime, + bool* outConflictingPointerActions); void addWindowTargetLocked(const sp& windowHandle, - int32_t targetFlags, BitSet32 pointerIds); - void addMonitoringTargetsLocked(); + int32_t targetFlags, BitSet32 pointerIds, Vector& inputTargets); + void addMonitoringTargetsLocked(Vector& inputTargets); + void pokeUserActivityLocked(const EventEntry* eventEntry); bool checkInjectionPermission(const sp& windowHandle, const InjectionState* injectionState); bool isWindowObscuredAtPointLocked(const sp& windowHandle, int32_t x, int32_t y) const; - bool isWindowFinishedWithPreviousInputLocked(const sp& windowHandle); + bool isWindowReadyForMoreInputLocked(nsecs_t currentTime, + const sp& windowHandle, const EventEntry* eventEntry); String8 getApplicationWindowLabelLocked(const sp& applicationHandle, const sp& windowHandle); @@ -1069,19 +1030,19 @@ private: // with the mutex held makes it easier to ensure that connection invariants are maintained. // If needed, the methods post commands to run later once the critical bits are done. void prepareDispatchCycleLocked(nsecs_t currentTime, const sp& connection, - EventEntry* eventEntry, const InputTarget* inputTarget, - bool resumeWithAppendedMotionSample); + EventEntry* eventEntry, const InputTarget* inputTarget); + void enqueueDispatchEntriesLocked(nsecs_t currentTime, const sp& connection, + EventEntry* eventEntry, const InputTarget* inputTarget); void enqueueDispatchEntryLocked(const sp& connection, - EventEntry* eventEntry, const InputTarget* inputTarget, - bool resumeWithAppendedMotionSample, int32_t dispatchMode); + EventEntry* eventEntry, const InputTarget* inputTarget, int32_t dispatchMode); void startDispatchCycleLocked(nsecs_t currentTime, const sp& connection); void finishDispatchCycleLocked(nsecs_t currentTime, const sp& connection, - bool handled); - void startNextDispatchCycleLocked(nsecs_t currentTime, const sp& connection); + uint32_t seq, bool handled); void abortBrokenDispatchCycleLocked(nsecs_t currentTime, const sp& connection, bool notify); - void drainOutboundQueueLocked(Connection* connection); - static int handleReceiveCallback(int receiveFd, int events, void* data); + void drainDispatchQueueLocked(Queue* queue); + void releaseDispatchEntryLocked(DispatchEntry* dispatchEntry); + static int handleReceiveCallback(int fd, int events, void* data); void synthesizeCancelationEventsForAllConnectionsLocked( const CancelationOptions& options); @@ -1109,16 +1070,14 @@ private: void deactivateConnectionLocked(Connection* connection); // Interesting events that we might like to log or tell the framework about. - void onDispatchCycleStartedLocked( - nsecs_t currentTime, const sp& connection); void onDispatchCycleFinishedLocked( - nsecs_t currentTime, const sp& connection, bool handled); + nsecs_t currentTime, const sp& connection, uint32_t seq, bool handled); void onDispatchCycleBrokenLocked( nsecs_t currentTime, const sp& connection); void onANRLocked( nsecs_t currentTime, const sp& applicationHandle, const sp& windowHandle, - nsecs_t eventTime, nsecs_t waitStartTime); + nsecs_t eventTime, nsecs_t waitStartTime, const char* reason); // Outbound policy interactions. void doNotifyConfigurationChangedInterruptible(CommandEntry* commandEntry); @@ -1136,6 +1095,9 @@ private: // Statistics gathering. void updateDispatchStatisticsLocked(nsecs_t currentTime, const EventEntry* entry, int32_t injectionResult, nsecs_t timeSpentWaitingForApplication); + void traceInboundQueueLengthLocked(); + void traceOutboundQueueLengthLocked(const sp& connection); + void traceWaitQueueLengthLocked(const sp& connection); }; /* Enqueues and dispatches input events, endlessly. */ diff --git a/widget/gonk/libui/InputListener.cpp b/widget/gonk/libui/InputListener.cpp index 0baa7f390bef..3b673f0adb4a 100644 --- a/widget/gonk/libui/InputListener.cpp +++ b/widget/gonk/libui/InputListener.cpp @@ -69,12 +69,12 @@ void NotifyKeyArgs::notify(const sp& listener) const { NotifyMotionArgs::NotifyMotionArgs(nsecs_t eventTime, int32_t deviceId, uint32_t source, uint32_t policyFlags, int32_t action, int32_t flags, int32_t metaState, int32_t buttonState, - int32_t edgeFlags, uint32_t pointerCount, + int32_t edgeFlags, int32_t displayId, uint32_t pointerCount, const PointerProperties* pointerProperties, const PointerCoords* pointerCoords, float xPrecision, float yPrecision, nsecs_t downTime) : eventTime(eventTime), deviceId(deviceId), source(source), policyFlags(policyFlags), action(action), flags(flags), metaState(metaState), buttonState(buttonState), - edgeFlags(edgeFlags), pointerCount(pointerCount), + edgeFlags(edgeFlags), displayId(displayId), pointerCount(pointerCount), xPrecision(xPrecision), yPrecision(yPrecision), downTime(downTime) { for (uint32_t i = 0; i < pointerCount; i++) { this->pointerProperties[i].copyFrom(pointerProperties[i]); @@ -87,7 +87,8 @@ NotifyMotionArgs::NotifyMotionArgs(const NotifyMotionArgs& other) : policyFlags(other.policyFlags), action(other.action), flags(other.flags), metaState(other.metaState), buttonState(other.buttonState), - edgeFlags(other.edgeFlags), pointerCount(other.pointerCount), + edgeFlags(other.edgeFlags), displayId(other.displayId), + pointerCount(other.pointerCount), xPrecision(other.xPrecision), yPrecision(other.yPrecision), downTime(other.downTime) { for (uint32_t i = 0; i < pointerCount; i++) { pointerProperties[i].copyFrom(other.pointerProperties[i]); @@ -103,14 +104,14 @@ void NotifyMotionArgs::notify(const sp& listener) const // --- NotifySwitchArgs --- NotifySwitchArgs::NotifySwitchArgs(nsecs_t eventTime, uint32_t policyFlags, - int32_t switchCode, int32_t switchValue) : + uint32_t switchValues, uint32_t switchMask) : eventTime(eventTime), policyFlags(policyFlags), - switchCode(switchCode), switchValue(switchValue) { + switchValues(switchValues), switchMask(switchMask) { } NotifySwitchArgs::NotifySwitchArgs(const NotifySwitchArgs& other) : eventTime(other.eventTime), policyFlags(other.policyFlags), - switchCode(other.switchCode), switchValue(other.switchValue) { + switchValues(other.switchValues), switchMask(other.switchMask) { } void NotifySwitchArgs::notify(const sp& listener) const { diff --git a/widget/gonk/libui/InputListener.h b/widget/gonk/libui/InputListener.h index c00b1fdf72fe..de799322f387 100644 --- a/widget/gonk/libui/InputListener.h +++ b/widget/gonk/libui/InputListener.h @@ -88,6 +88,7 @@ struct NotifyMotionArgs : public NotifyArgs { int32_t metaState; int32_t buttonState; int32_t edgeFlags; + int32_t displayId; uint32_t pointerCount; PointerProperties pointerProperties[MAX_POINTERS]; PointerCoords pointerCoords[MAX_POINTERS]; @@ -99,7 +100,7 @@ struct NotifyMotionArgs : public NotifyArgs { NotifyMotionArgs(nsecs_t eventTime, int32_t deviceId, uint32_t source, uint32_t policyFlags, int32_t action, int32_t flags, int32_t metaState, int32_t buttonState, - int32_t edgeFlags, uint32_t pointerCount, + int32_t edgeFlags, int32_t displayId, uint32_t pointerCount, const PointerProperties* pointerProperties, const PointerCoords* pointerCoords, float xPrecision, float yPrecision, nsecs_t downTime); @@ -115,13 +116,13 @@ struct NotifyMotionArgs : public NotifyArgs { struct NotifySwitchArgs : public NotifyArgs { nsecs_t eventTime; uint32_t policyFlags; - int32_t switchCode; - int32_t switchValue; + uint32_t switchValues; + uint32_t switchMask; inline NotifySwitchArgs() { } NotifySwitchArgs(nsecs_t eventTime, uint32_t policyFlags, - int32_t switchCode, int32_t switchValue); + uint32_t switchValues, uint32_t switchMask); NotifySwitchArgs(const NotifySwitchArgs& other); diff --git a/widget/gonk/libui/InputManager.cpp b/widget/gonk/libui/InputManager.cpp new file mode 100644 index 000000000000..91af056bfe3b --- /dev/null +++ b/widget/gonk/libui/InputManager.cpp @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "InputManager" + +//#define LOG_NDEBUG 0 + +#include "InputManager.h" + +#include "cutils_log.h" + +namespace android { + +InputManager::InputManager( + const sp& eventHub, + const sp& readerPolicy, + const sp& dispatcherPolicy) { + mDispatcher = new InputDispatcher(dispatcherPolicy); + mReader = new InputReader(eventHub, readerPolicy, mDispatcher); + initialize(); +} + +InputManager::InputManager( + const sp& reader, + const sp& dispatcher) : + mReader(reader), + mDispatcher(dispatcher) { + initialize(); +} + +InputManager::~InputManager() { + stop(); +} + +void InputManager::initialize() { + mReaderThread = new InputReaderThread(mReader); + mDispatcherThread = new InputDispatcherThread(mDispatcher); +} + +status_t InputManager::start() { + status_t result = mDispatcherThread->run("InputDispatcher", PRIORITY_URGENT_DISPLAY); + if (result) { + ALOGE("Could not start InputDispatcher thread due to error %d.", result); + return result; + } + + result = mReaderThread->run("InputReader", PRIORITY_URGENT_DISPLAY); + if (result) { + ALOGE("Could not start InputReader thread due to error %d.", result); + + mDispatcherThread->requestExit(); + return result; + } + + return OK; +} + +status_t InputManager::stop() { + status_t result = mReaderThread->requestExitAndWait(); + if (result) { + ALOGW("Could not stop InputReader thread due to error %d.", result); + } + + result = mDispatcherThread->requestExitAndWait(); + if (result) { + ALOGW("Could not stop InputDispatcher thread due to error %d.", result); + } + + return OK; +} + +sp InputManager::getReader() { + return mReader; +} + +sp InputManager::getDispatcher() { + return mDispatcher; +} + +} // namespace android diff --git a/widget/gonk/libui/InputManager.h b/widget/gonk/libui/InputManager.h new file mode 100644 index 000000000000..15a5176ecda2 --- /dev/null +++ b/widget/gonk/libui/InputManager.h @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UI_INPUT_MANAGER_H +#define _UI_INPUT_MANAGER_H + +/** + * Native input manager. + */ + +#include "EventHub.h" +#include "InputReader.h" +#include "InputDispatcher.h" + +#include "Input.h" +#include "InputTransport.h" +#include +#include +#include +#include +#include + +namespace android { + +/* + * The input manager is the core of the system event processing. + * + * The input manager uses two threads. + * + * 1. The InputReaderThread (called "InputReader") reads and preprocesses raw input events, + * applies policy, and posts messages to a queue managed by the DispatcherThread. + * 2. The InputDispatcherThread (called "InputDispatcher") thread waits for new events on the + * queue and asynchronously dispatches them to applications. + * + * By design, the InputReaderThread class and InputDispatcherThread class do not share any + * internal state. Moreover, all communication is done one way from the InputReaderThread + * into the InputDispatcherThread and never the reverse. Both classes may interact with the + * InputDispatchPolicy, however. + * + * The InputManager class never makes any calls into Java itself. Instead, the + * InputDispatchPolicy is responsible for performing all external interactions with the + * system, including calling DVM services. + */ +class InputManagerInterface : public virtual RefBase { +protected: + InputManagerInterface() { } + virtual ~InputManagerInterface() { } + +public: + /* Starts the input manager threads. */ + virtual status_t start() = 0; + + /* Stops the input manager threads and waits for them to exit. */ + virtual status_t stop() = 0; + + /* Gets the input reader. */ + virtual sp getReader() = 0; + + /* Gets the input dispatcher. */ + virtual sp getDispatcher() = 0; +}; + +class InputManager : public InputManagerInterface { +protected: + virtual ~InputManager(); + +public: + InputManager( + const sp& eventHub, + const sp& readerPolicy, + const sp& dispatcherPolicy); + + // (used for testing purposes) + InputManager( + const sp& reader, + const sp& dispatcher); + + virtual status_t start(); + virtual status_t stop(); + + virtual sp getReader(); + virtual sp getDispatcher(); + +private: + sp mReader; + sp mReaderThread; + + sp mDispatcher; + sp mDispatcherThread; + + void initialize(); +}; + +} // namespace android + +#endif // _UI_INPUT_MANAGER_H diff --git a/widget/gonk/libui/InputReader.cpp b/widget/gonk/libui/InputReader.cpp index a5215562c402..2ccde77cd178 100644 --- a/widget/gonk/libui/InputReader.cpp +++ b/widget/gonk/libui/InputReader.cpp @@ -17,6 +17,7 @@ #define LOG_TAG "InputReader" //#define LOG_NDEBUG 0 +#include "cutils_log.h" // Log debug messages for each raw event received from the EventHub. #define DEBUG_RAW_EVENTS 0 @@ -36,9 +37,11 @@ // Log debug messages about gesture detection. #define DEBUG_GESTURES 0 +// Log debug messages about the vibrator. +#define DEBUG_VIBRATOR 0 + #include "InputReader.h" -#include "cutils_log.h" #include "Keyboard.h" #include "VirtualKeyMap.h" @@ -200,34 +203,18 @@ static void synthesizeButtonKeys(InputReaderContext* context, int32_t action, // --- InputReaderConfiguration --- -bool InputReaderConfiguration::getDisplayInfo(int32_t displayId, bool external, - int32_t* width, int32_t* height, int32_t* orientation) const { - if (displayId == 0) { - const DisplayInfo& info = external ? mExternalDisplay : mInternalDisplay; - if (info.width > 0 && info.height > 0) { - if (width) { - *width = info.width; - } - if (height) { - *height = info.height; - } - if (orientation) { - *orientation = info.orientation; - } - return true; - } +bool InputReaderConfiguration::getDisplayInfo(bool external, DisplayViewport* outViewport) const { + const DisplayViewport& viewport = external ? mExternalDisplay : mInternalDisplay; + if (viewport.displayId >= 0) { + *outViewport = viewport; + return true; } return false; } -void InputReaderConfiguration::setDisplayInfo(int32_t displayId, bool external, - int32_t width, int32_t height, int32_t orientation) { - if (displayId == 0) { - DisplayInfo& info = external ? mExternalDisplay : mInternalDisplay; - info.width = width; - info.height = height; - info.orientation = orientation; - } +void InputReaderConfiguration::setDisplayInfo(bool external, const DisplayViewport& viewport) { + DisplayViewport& v = external ? mExternalDisplay : mInternalDisplay; + v = viewport; } @@ -237,7 +224,8 @@ InputReader::InputReader(const sp& eventHub, const sp& policy, const sp& listener) : mContext(this), mEventHub(eventHub), mPolicy(policy), - mGlobalMetaState(0), mDisableVirtualKeysTimeout(LLONG_MIN), mNextTimeout(LLONG_MAX), + mGlobalMetaState(0), mGeneration(1), + mDisableVirtualKeysTimeout(LLONG_MIN), mNextTimeout(LLONG_MAX), mConfigurationChangesToRefresh(0) { mQueuedListener = new QueuedInputListener(listener); @@ -246,7 +234,6 @@ InputReader::InputReader(const sp& eventHub, refreshConfigurationLocked(0); updateGlobalMetaStateLocked(); - updateInputConfigurationLocked(); } // release lock } @@ -257,18 +244,22 @@ InputReader::~InputReader() { } void InputReader::loopOnce() { + int32_t oldGeneration; int32_t timeoutMillis; + bool inputDevicesChanged = false; + Vector inputDevices; { // acquire lock AutoMutex _l(mLock); + oldGeneration = mGeneration; + timeoutMillis = -1; + uint32_t changes = mConfigurationChangesToRefresh; if (changes) { mConfigurationChangesToRefresh = 0; + timeoutMillis = 0; refreshConfigurationLocked(changes); - } - - timeoutMillis = -1; - if (mNextTimeout != LLONG_MAX) { + } else if (mNextTimeout != LLONG_MAX) { nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); timeoutMillis = toMillisecondTimeoutDelay(now, mNextTimeout); } @@ -278,20 +269,34 @@ void InputReader::loopOnce() { { // acquire lock AutoMutex _l(mLock); + mReaderIsAliveCondition.broadcast(); if (count) { processEventsLocked(mEventBuffer, count); } - if (!count || timeoutMillis == 0) { + + if (mNextTimeout != LLONG_MAX) { nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); + if (now >= mNextTimeout) { #if DEBUG_RAW_EVENTS - ALOGD("Timeout expired, latency=%0.3fms", (now - mNextTimeout) * 0.000001f); + ALOGD("Timeout expired, latency=%0.3fms", (now - mNextTimeout) * 0.000001f); #endif - mNextTimeout = LLONG_MAX; - timeoutExpiredLocked(now); + mNextTimeout = LLONG_MAX; + timeoutExpiredLocked(now); + } + } + + if (oldGeneration != mGeneration) { + inputDevicesChanged = true; + getInputDevicesLocked(inputDevices); } } // release lock + // Send out a message that the describes the changed input devices. + if (inputDevicesChanged) { + mPolicy->notifyInputDevicesChanged(inputDevices); + } + // Flush queued events out to the listener. // This must happen outside of the lock because the listener could potentially call // back into the InputReader's methods, such as getScanCodeState, or become blocked @@ -341,41 +346,43 @@ void InputReader::processEventsLocked(const RawEvent* rawEvents, size_t count) { } void InputReader::addDeviceLocked(nsecs_t when, int32_t deviceId) { - String8 name = mEventHub->getDeviceName(deviceId); + ssize_t deviceIndex = mDevices.indexOfKey(deviceId); + if (deviceIndex >= 0) { + ALOGW("Ignoring spurious device added event for deviceId %d.", deviceId); + return; + } + + InputDeviceIdentifier identifier = mEventHub->getDeviceIdentifier(deviceId); uint32_t classes = mEventHub->getDeviceClasses(deviceId); - InputDevice* device = createDeviceLocked(deviceId, name, classes); + InputDevice* device = createDeviceLocked(deviceId, identifier, classes); device->configure(when, &mConfig, 0); device->reset(when); if (device->isIgnored()) { - ALOGI("Device added: id=%d, name='%s' (ignored non-input device)", deviceId, name.string()); + ALOGI("Device added: id=%d, name='%s' (ignored non-input device)", deviceId, + identifier.name.string()); } else { - ALOGI("Device added: id=%d, name='%s', sources=0x%08x", deviceId, name.string(), - device->getSources()); + ALOGI("Device added: id=%d, name='%s', sources=0x%08x", deviceId, + identifier.name.string(), device->getSources()); } - ssize_t deviceIndex = mDevices.indexOfKey(deviceId); - if (deviceIndex < 0) { - mDevices.add(deviceId, device); - } else { - ALOGW("Ignoring spurious device added event for deviceId %d.", deviceId); - delete device; - return; - } + mDevices.add(deviceId, device); + bumpGenerationLocked(); } void InputReader::removeDeviceLocked(nsecs_t when, int32_t deviceId) { InputDevice* device = NULL; ssize_t deviceIndex = mDevices.indexOfKey(deviceId); - if (deviceIndex >= 0) { - device = mDevices.valueAt(deviceIndex); - mDevices.removeItemsAt(deviceIndex, 1); - } else { + if (deviceIndex < 0) { ALOGW("Ignoring spurious device removed event for deviceId %d.", deviceId); return; } + device = mDevices.valueAt(deviceIndex); + mDevices.removeItemsAt(deviceIndex, 1); + bumpGenerationLocked(); + if (device->isIgnored()) { ALOGI("Device removed: id=%d, name='%s' (ignored non-input device)", device->getId(), device->getName().string()); @@ -389,8 +396,9 @@ void InputReader::removeDeviceLocked(nsecs_t when, int32_t deviceId) { } InputDevice* InputReader::createDeviceLocked(int32_t deviceId, - const String8& name, uint32_t classes) { - InputDevice* device = new InputDevice(&mContext, deviceId, name, classes); + const InputDeviceIdentifier& identifier, uint32_t classes) { + InputDevice* device = new InputDevice(&mContext, deviceId, bumpGenerationLocked(), + identifier, classes); // External devices. if (classes & INPUT_DEVICE_CLASS_EXTERNAL) { @@ -402,6 +410,11 @@ InputDevice* InputReader::createDeviceLocked(int32_t deviceId, device->addMapper(new SwitchInputMapper(device)); } + // Vibrator-like devices. + if (classes & INPUT_DEVICE_CLASS_VIBRATOR) { + device->addMapper(new VibratorInputMapper(device)); + } + // Keyboard-like devices. uint32_t keyboardSource = 0; int32_t keyboardType = AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC; @@ -472,9 +485,6 @@ void InputReader::handleConfigurationChangedLocked(nsecs_t when) { // Reset global meta state because it depends on the list of all configured devices. updateGlobalMetaStateLocked(); - // Update input configuration. - updateInputConfigurationLocked(); - // Enqueue configuration changed. NotifyConfigurationChangedArgs args(when); mQueuedListener->notifyConfigurationChanged(&args); @@ -512,34 +522,6 @@ int32_t InputReader::getGlobalMetaStateLocked() { return mGlobalMetaState; } -void InputReader::updateInputConfigurationLocked() { - int32_t touchScreenConfig = InputConfiguration::TOUCHSCREEN_NOTOUCH; - int32_t keyboardConfig = InputConfiguration::KEYBOARD_NOKEYS; - int32_t navigationConfig = InputConfiguration::NAVIGATION_NONAV; - InputDeviceInfo deviceInfo; - for (size_t i = 0; i < mDevices.size(); i++) { - InputDevice* device = mDevices.valueAt(i); - device->getDeviceInfo(& deviceInfo); - uint32_t sources = deviceInfo.getSources(); - - if ((sources & AINPUT_SOURCE_TOUCHSCREEN) == AINPUT_SOURCE_TOUCHSCREEN) { - touchScreenConfig = InputConfiguration::TOUCHSCREEN_FINGER; - } - if ((sources & AINPUT_SOURCE_TRACKBALL) == AINPUT_SOURCE_TRACKBALL) { - navigationConfig = InputConfiguration::NAVIGATION_TRACKBALL; - } else if ((sources & AINPUT_SOURCE_DPAD) == AINPUT_SOURCE_DPAD) { - navigationConfig = InputConfiguration::NAVIGATION_DPAD; - } - if (deviceInfo.getKeyboardType() == AINPUT_KEYBOARD_TYPE_ALPHABETIC) { - keyboardConfig = InputConfiguration::KEYBOARD_QWERTY; - } - } - - mInputConfiguration.touchScreen = touchScreenConfig; - mInputConfiguration.keyboard = keyboardConfig; - mInputConfiguration.navigation = navigationConfig; -} - void InputReader::disableVirtualKeysUntilLocked(nsecs_t time) { mDisableVirtualKeysTimeout = time; } @@ -568,42 +550,28 @@ void InputReader::fadePointerLocked() { void InputReader::requestTimeoutAtTimeLocked(nsecs_t when) { if (when < mNextTimeout) { mNextTimeout = when; + mEventHub->wake(); } } -void InputReader::getInputConfiguration(InputConfiguration* outConfiguration) { - AutoMutex _l(mLock); - - *outConfiguration = mInputConfiguration; +int32_t InputReader::bumpGenerationLocked() { + return ++mGeneration; } -status_t InputReader::getInputDeviceInfo(int32_t deviceId, InputDeviceInfo* outDeviceInfo) { +void InputReader::getInputDevices(Vector& outInputDevices) { AutoMutex _l(mLock); - - ssize_t deviceIndex = mDevices.indexOfKey(deviceId); - if (deviceIndex < 0) { - return NAME_NOT_FOUND; - } - - InputDevice* device = mDevices.valueAt(deviceIndex); - if (device->isIgnored()) { - return NAME_NOT_FOUND; - } - - device->getDeviceInfo(outDeviceInfo); - return OK; + getInputDevicesLocked(outInputDevices); } -void InputReader::getInputDeviceIds(Vector& outDeviceIds) { - AutoMutex _l(mLock); - - outDeviceIds.clear(); +void InputReader::getInputDevicesLocked(Vector& outInputDevices) { + outInputDevices.clear(); size_t numDevices = mDevices.size(); for (size_t i = 0; i < numDevices; i++) { InputDevice* device = mDevices.valueAt(i); if (!device->isIgnored()) { - outDeviceIds.add(device->getId()); + outInputDevices.push(); + device->getDeviceInfo(&outInputDevices.editTop()); } } } @@ -644,9 +612,13 @@ int32_t InputReader::getStateLocked(int32_t deviceId, uint32_t sourceMask, int32 for (size_t i = 0; i < numDevices; i++) { InputDevice* device = mDevices.valueAt(i); if (! device->isIgnored() && sourcesMatchMask(device->getSources(), sourceMask)) { - result = (device->*getStateFunc)(sourceMask, code); - if (result >= AKEY_STATE_DOWN) { - return result; + // If any device reports AKEY_STATE_DOWN or AKEY_STATE_VIRTUAL, return that + // value. Otherwise, return AKEY_STATE_UP as long as one device reports it. + int32_t currentResult = (device->*getStateFunc)(sourceMask, code); + if (currentResult >= AKEY_STATE_DOWN) { + return currentResult; + } else if (currentResult == AKEY_STATE_UP) { + result = currentResult; } } } @@ -700,6 +672,27 @@ void InputReader::requestRefreshConfiguration(uint32_t changes) { } } +void InputReader::vibrate(int32_t deviceId, const nsecs_t* pattern, size_t patternSize, + ssize_t repeat, int32_t token) { + AutoMutex _l(mLock); + + ssize_t deviceIndex = mDevices.indexOfKey(deviceId); + if (deviceIndex >= 0) { + InputDevice* device = mDevices.valueAt(deviceIndex); + device->vibrate(pattern, patternSize, repeat, token); + } +} + +void InputReader::cancelVibrate(int32_t deviceId, int32_t token) { + AutoMutex _l(mLock); + + ssize_t deviceIndex = mDevices.indexOfKey(deviceId); + if (deviceIndex >= 0) { + InputDevice* device = mDevices.valueAt(deviceIndex); + device->cancelVibrate(token); + } +} + void InputReader::dump(String8& dump) { AutoMutex _l(mLock); @@ -768,6 +761,8 @@ void InputReader::dump(String8& dump) { void InputReader::monitor() { // Acquire and release the lock to ensure that the reader has not deadlocked. mLock.lock(); + mEventHub->wake(); + mReaderIsAliveCondition.wait(mLock); mLock.unlock(); // Check the EventHub @@ -812,6 +807,11 @@ void InputReader::ContextImpl::requestTimeoutAtTime(nsecs_t when) { mReader->requestTimeoutAtTimeLocked(when); } +int32_t InputReader::ContextImpl::bumpGeneration() { + // lock is already held by the input loop + return mReader->bumpGenerationLocked(); +} + InputReaderPolicyInterface* InputReader::ContextImpl::getPolicy() { return mReader->mPolicy.get(); } @@ -842,9 +842,10 @@ bool InputReaderThread::threadLoop() { // --- InputDevice --- -InputDevice::InputDevice(InputReaderContext* context, int32_t id, const String8& name, - uint32_t classes) : - mContext(context), mId(id), mName(name), mClasses(classes), +InputDevice::InputDevice(InputReaderContext* context, int32_t id, int32_t generation, + const InputDeviceIdentifier& identifier, uint32_t classes) : + mContext(context), mId(id), mGeneration(generation), + mIdentifier(identifier), mClasses(classes), mSources(0), mIsExternal(false), mDropUntilNextSync(false) { } @@ -861,7 +862,8 @@ void InputDevice::dump(String8& dump) { getDeviceInfo(& deviceInfo); dump.appendFormat(INDENT "Device %d: %s\n", deviceInfo.getId(), - deviceInfo.getName().string()); + deviceInfo.getDisplayName().string()); + dump.appendFormat(INDENT2 "Generation: %d\n", mGeneration); dump.appendFormat(INDENT2 "IsExternal: %s\n", toString(mIsExternal)); dump.appendFormat(INDENT2 "Sources: 0x%08x\n", deviceInfo.getSources()); dump.appendFormat(INDENT2 "KeyboardType: %d\n", deviceInfo.getKeyboardType()); @@ -880,8 +882,9 @@ void InputDevice::dump(String8& dump) { snprintf(name, sizeof(name), "%d", range.axis); } dump.appendFormat(INDENT3 "%s: source=0x%08x, " - "min=%0.3f, max=%0.3f, flat=%0.3f, fuzz=%0.3f\n", - name, range.source, range.min, range.max, range.flat, range.fuzz); + "min=%0.3f, max=%0.3f, flat=%0.3f, fuzz=%0.3f, resolution=%0.3f\n", + name, range.source, range.min, range.max, range.flat, range.fuzz, + range.resolution); } } @@ -904,6 +907,26 @@ void InputDevice::configure(nsecs_t when, const InputReaderConfiguration* config mContext->getEventHub()->getConfiguration(mId, &mConfiguration); } + if (!changes || (changes & InputReaderConfiguration::CHANGE_KEYBOARD_LAYOUTS)) { + if (!(mClasses & INPUT_DEVICE_CLASS_VIRTUAL)) { + sp keyboardLayout = + mContext->getPolicy()->getKeyboardLayoutOverlay(mIdentifier.descriptor); + if (mContext->getEventHub()->setKeyboardLayoutOverlay(mId, keyboardLayout)) { + bumpGeneration(); + } + } + } + + if (!changes || (changes & InputReaderConfiguration::CHANGE_DEVICE_ALIAS)) { + if (!(mClasses & INPUT_DEVICE_CLASS_VIRTUAL)) { + String8 alias = mContext->getPolicy()->getDeviceAlias(mIdentifier); + if (mAlias != alias) { + mAlias = alias; + bumpGeneration(); + } + } + } + size_t numMappers = mMappers.size(); for (size_t i = 0; i < numMappers; i++) { InputMapper* mapper = mMappers[i]; @@ -934,14 +957,13 @@ void InputDevice::process(const RawEvent* rawEvents, size_t count) { size_t numMappers = mMappers.size(); for (const RawEvent* rawEvent = rawEvents; count--; rawEvent++) { #if DEBUG_RAW_EVENTS - ALOGD("Input event: device=%d type=0x%04x scancode=0x%04x " - "keycode=0x%04x value=0x%08x flags=0x%08x", - rawEvent->deviceId, rawEvent->type, rawEvent->scanCode, rawEvent->keyCode, - rawEvent->value, rawEvent->flags); + ALOGD("Input event: device=%d type=0x%04x code=0x%04x value=0x%08x when=%lld", + rawEvent->deviceId, rawEvent->type, rawEvent->code, rawEvent->value, + rawEvent->when); #endif if (mDropUntilNextSync) { - if (rawEvent->type == EV_SYN && rawEvent->scanCode == SYN_REPORT) { + if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) { mDropUntilNextSync = false; #if DEBUG_RAW_EVENTS ALOGD("Recovered from input event buffer overrun."); @@ -951,8 +973,8 @@ void InputDevice::process(const RawEvent* rawEvents, size_t count) { ALOGD("Dropped input event while waiting for next input sync."); #endif } - } else if (rawEvent->type == EV_SYN && rawEvent->scanCode == SYN_DROPPED) { - ALOGI("Detected input event buffer overrun for device %s.", mName.string()); + } else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_DROPPED) { + ALOGI("Detected input event buffer overrun for device %s.", getName().string()); mDropUntilNextSync = true; reset(rawEvent->when); } else { @@ -973,7 +995,7 @@ void InputDevice::timeoutExpired(nsecs_t when) { } void InputDevice::getDeviceInfo(InputDeviceInfo* outDeviceInfo) { - outDeviceInfo->initialize(mId, mName); + outDeviceInfo->initialize(mId, mGeneration, mIdentifier, mAlias, mIsExternal); size_t numMappers = mMappers.size(); for (size_t i = 0; i < numMappers; i++) { @@ -1000,9 +1022,13 @@ int32_t InputDevice::getState(uint32_t sourceMask, int32_t code, GetStateFunc ge for (size_t i = 0; i < numMappers; i++) { InputMapper* mapper = mMappers[i]; if (sourcesMatchMask(mapper->getSources(), sourceMask)) { - result = (mapper->*getStateFunc)(sourceMask, code); - if (result >= AKEY_STATE_DOWN) { - return result; + // If any mapper reports AKEY_STATE_DOWN or AKEY_STATE_VIRTUAL, return that + // value. Otherwise, return AKEY_STATE_UP as long as one mapper reports it. + int32_t currentResult = (mapper->*getStateFunc)(sourceMask, code); + if (currentResult >= AKEY_STATE_DOWN) { + return currentResult; + } else if (currentResult == AKEY_STATE_UP) { + result = currentResult; } } } @@ -1022,6 +1048,23 @@ bool InputDevice::markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, return result; } +void InputDevice::vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat, + int32_t token) { + size_t numMappers = mMappers.size(); + for (size_t i = 0; i < numMappers; i++) { + InputMapper* mapper = mMappers[i]; + mapper->vibrate(pattern, patternSize, repeat, token); + } +} + +void InputDevice::cancelVibrate(int32_t token) { + size_t numMappers = mMappers.size(); + for (size_t i = 0; i < numMappers; i++) { + InputMapper* mapper = mMappers[i]; + mapper->cancelVibrate(token); + } +} + int32_t InputDevice::getMetaState() { int32_t result = 0; size_t numMappers = mMappers.size(); @@ -1040,6 +1083,10 @@ void InputDevice::fadePointer() { } } +void InputDevice::bumpGeneration() { + mGeneration = mContext->bumpGeneration(); +} + void InputDevice::notifyReset(nsecs_t when) { NotifyDeviceResetArgs args(when, mId); mContext->getListener()->notifyDeviceReset(&args); @@ -1076,7 +1123,7 @@ void CursorButtonAccumulator::clearButtons() { void CursorButtonAccumulator::process(const RawEvent* rawEvent) { if (rawEvent->type == EV_KEY) { - switch (rawEvent->scanCode) { + switch (rawEvent->code) { case BTN_LEFT: mBtnLeft = rawEvent->value; break; @@ -1143,7 +1190,7 @@ void CursorMotionAccumulator::clearRelativeAxes() { void CursorMotionAccumulator::process(const RawEvent* rawEvent) { if (rawEvent->type == EV_REL) { - switch (rawEvent->scanCode) { + switch (rawEvent->code) { case REL_X: mRelX = rawEvent->value; break; @@ -1182,7 +1229,7 @@ void CursorScrollAccumulator::clearRelativeAxes() { void CursorScrollAccumulator::process(const RawEvent* rawEvent) { if (rawEvent->type == EV_REL) { - switch (rawEvent->scanCode) { + switch (rawEvent->code) { case REL_WHEEL: mRelWheel = rawEvent->value; break; @@ -1201,12 +1248,17 @@ void CursorScrollAccumulator::finishSync() { // --- TouchButtonAccumulator --- TouchButtonAccumulator::TouchButtonAccumulator() : - mHaveBtnTouch(false) { + mHaveBtnTouch(false), mHaveStylus(false) { clearButtons(); } void TouchButtonAccumulator::configure(InputDevice* device) { mHaveBtnTouch = device->hasKey(BTN_TOUCH); + mHaveStylus = device->hasKey(BTN_TOOL_PEN) + || device->hasKey(BTN_TOOL_RUBBER) + || device->hasKey(BTN_TOOL_BRUSH) + || device->hasKey(BTN_TOOL_PENCIL) + || device->hasKey(BTN_TOOL_AIRBRUSH); } void TouchButtonAccumulator::reset(InputDevice* device) { @@ -1245,7 +1297,7 @@ void TouchButtonAccumulator::clearButtons() { void TouchButtonAccumulator::process(const RawEvent* rawEvent) { if (rawEvent->type == EV_KEY) { - switch (rawEvent->scanCode) { + switch (rawEvent->code) { case BTN_TOUCH: mBtnTouch = rawEvent->value; break; @@ -1330,6 +1382,10 @@ bool TouchButtonAccumulator::isHovering() const { return mHaveBtnTouch && !mBtnTouch; } +bool TouchButtonAccumulator::hasStylus() const { + return mHaveStylus; +} + // --- RawPointerAxes --- @@ -1451,7 +1507,7 @@ void SingleTouchMotionAccumulator::clearAbsoluteAxes() { void SingleTouchMotionAccumulator::process(const RawEvent* rawEvent) { if (rawEvent->type == EV_ABS) { - switch (rawEvent->scanCode) { + switch (rawEvent->code) { case ABS_X: mAbsX = rawEvent->value; break; @@ -1481,16 +1537,19 @@ void SingleTouchMotionAccumulator::process(const RawEvent* rawEvent) { // --- MultiTouchMotionAccumulator --- MultiTouchMotionAccumulator::MultiTouchMotionAccumulator() : - mCurrentSlot(-1), mSlots(NULL), mSlotCount(0), mUsingSlotsProtocol(false) { + mCurrentSlot(-1), mSlots(NULL), mSlotCount(0), mUsingSlotsProtocol(false), + mHaveStylus(false) { } MultiTouchMotionAccumulator::~MultiTouchMotionAccumulator() { delete[] mSlots; } -void MultiTouchMotionAccumulator::configure(size_t slotCount, bool usingSlotsProtocol) { +void MultiTouchMotionAccumulator::configure(InputDevice* device, + size_t slotCount, bool usingSlotsProtocol) { mSlotCount = slotCount; mUsingSlotsProtocol = usingSlotsProtocol; + mHaveStylus = device->hasAbsoluteAxis(ABS_MT_TOOL_TYPE); delete[] mSlots; mSlots = new Slot[slotCount]; @@ -1535,7 +1594,7 @@ void MultiTouchMotionAccumulator::process(const RawEvent* rawEvent) { if (rawEvent->type == EV_ABS) { bool newSlot = false; if (mUsingSlotsProtocol) { - if (rawEvent->scanCode == ABS_MT_SLOT) { + if (rawEvent->code == ABS_MT_SLOT) { mCurrentSlot = rawEvent->value; newSlot = true; } @@ -1554,7 +1613,7 @@ void MultiTouchMotionAccumulator::process(const RawEvent* rawEvent) { } else { Slot* slot = &mSlots[mCurrentSlot]; - switch (rawEvent->scanCode) { + switch (rawEvent->code) { case ABS_MT_POSITION_X: slot->mInUse = true; slot->mAbsMTPositionX = rawEvent->value; @@ -1610,7 +1669,7 @@ void MultiTouchMotionAccumulator::process(const RawEvent* rawEvent) { break; } } - } else if (rawEvent->type == EV_SYN && rawEvent->scanCode == SYN_MT_REPORT) { + } else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_MT_REPORT) { // MultiTouch Sync: The driver has returned all data for *one* of the pointers. mCurrentSlot += 1; } @@ -1622,6 +1681,10 @@ void MultiTouchMotionAccumulator::finishSync() { } } +bool MultiTouchMotionAccumulator::hasStylus() const { + return mHaveStylus; +} + // --- MultiTouchMotionAccumulator::Slot --- @@ -1703,6 +1766,13 @@ bool InputMapper::markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, return false; } +void InputMapper::vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat, + int32_t token) { +} + +void InputMapper::cancelVibrate(int32_t token) { +} + int32_t InputMapper::getMetaState() { return 0; } @@ -1714,6 +1784,10 @@ status_t InputMapper::getAbsoluteAxisInfo(int32_t axis, RawAbsoluteAxisInfo* axi return getEventHub()->getAbsoluteAxisInfo(getDeviceId(), axis, axisInfo); } +void InputMapper::bumpGeneration() { + mDevice->bumpGeneration(); +} + void InputMapper::dumpRawAbsoluteAxisInfo(String8& dump, const RawAbsoluteAxisInfo& axis, const char* name) { if (axis.valid) { @@ -1728,7 +1802,7 @@ void InputMapper::dumpRawAbsoluteAxisInfo(String8& dump, // --- SwitchInputMapper --- SwitchInputMapper::SwitchInputMapper(InputDevice* device) : - InputMapper(device) { + InputMapper(device), mUpdatedSwitchValues(0), mUpdatedSwitchMask(0) { } SwitchInputMapper::~SwitchInputMapper() { @@ -1741,14 +1815,33 @@ uint32_t SwitchInputMapper::getSources() { void SwitchInputMapper::process(const RawEvent* rawEvent) { switch (rawEvent->type) { case EV_SW: - processSwitch(rawEvent->when, rawEvent->scanCode, rawEvent->value); + processSwitch(rawEvent->code, rawEvent->value); break; + + case EV_SYN: + if (rawEvent->code == SYN_REPORT) { + sync(rawEvent->when); + } } } -void SwitchInputMapper::processSwitch(nsecs_t when, int32_t switchCode, int32_t switchValue) { - NotifySwitchArgs args(when, 0, switchCode, switchValue); - getListener()->notifySwitch(&args); +void SwitchInputMapper::processSwitch(int32_t switchCode, int32_t switchValue) { + if (switchCode >= 0 && switchCode < 32) { + if (switchValue) { + mUpdatedSwitchValues |= 1 << switchCode; + } + mUpdatedSwitchMask |= 1 << switchCode; + } +} + +void SwitchInputMapper::sync(nsecs_t when) { + if (mUpdatedSwitchMask) { + NotifySwitchArgs args(when, 0, mUpdatedSwitchValues, mUpdatedSwitchMask); + getListener()->notifySwitch(&args); + + mUpdatedSwitchValues = 0; + mUpdatedSwitchMask = 0; + } } int32_t SwitchInputMapper::getSwitchState(uint32_t sourceMask, int32_t switchCode) { @@ -1756,6 +1849,120 @@ int32_t SwitchInputMapper::getSwitchState(uint32_t sourceMask, int32_t switchCod } +// --- VibratorInputMapper --- + +VibratorInputMapper::VibratorInputMapper(InputDevice* device) : + InputMapper(device), mVibrating(false) { +} + +VibratorInputMapper::~VibratorInputMapper() { +} + +uint32_t VibratorInputMapper::getSources() { + return 0; +} + +void VibratorInputMapper::populateDeviceInfo(InputDeviceInfo* info) { + InputMapper::populateDeviceInfo(info); + + info->setVibrator(true); +} + +void VibratorInputMapper::process(const RawEvent* rawEvent) { + // TODO: Handle FF_STATUS, although it does not seem to be widely supported. +} + +void VibratorInputMapper::vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat, + int32_t token) { +#if DEBUG_VIBRATOR + String8 patternStr; + for (size_t i = 0; i < patternSize; i++) { + if (i != 0) { + patternStr.append(", "); + } + patternStr.appendFormat("%lld", pattern[i]); + } + ALOGD("vibrate: deviceId=%d, pattern=[%s], repeat=%ld, token=%d", + getDeviceId(), patternStr.string(), repeat, token); +#endif + + mVibrating = true; + memcpy(mPattern, pattern, patternSize * sizeof(nsecs_t)); + mPatternSize = patternSize; + mRepeat = repeat; + mToken = token; + mIndex = -1; + + nextStep(); +} + +void VibratorInputMapper::cancelVibrate(int32_t token) { +#if DEBUG_VIBRATOR + ALOGD("cancelVibrate: deviceId=%d, token=%d", getDeviceId(), token); +#endif + + if (mVibrating && mToken == token) { + stopVibrating(); + } +} + +void VibratorInputMapper::timeoutExpired(nsecs_t when) { + if (mVibrating) { + if (when >= mNextStepTime) { + nextStep(); + } else { + getContext()->requestTimeoutAtTime(mNextStepTime); + } + } +} + +void VibratorInputMapper::nextStep() { + mIndex += 1; + if (size_t(mIndex) >= mPatternSize) { + if (mRepeat < 0) { + // We are done. + stopVibrating(); + return; + } + mIndex = mRepeat; + } + + bool vibratorOn = mIndex & 1; + nsecs_t duration = mPattern[mIndex]; + if (vibratorOn) { +#if DEBUG_VIBRATOR + ALOGD("nextStep: sending vibrate deviceId=%d, duration=%lld", + getDeviceId(), duration); +#endif + getEventHub()->vibrate(getDeviceId(), duration); + } else { +#if DEBUG_VIBRATOR + ALOGD("nextStep: sending cancel vibrate deviceId=%d", getDeviceId()); +#endif + getEventHub()->cancelVibrate(getDeviceId()); + } + nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); + mNextStepTime = now + duration; + getContext()->requestTimeoutAtTime(mNextStepTime); +#if DEBUG_VIBRATOR + ALOGD("nextStep: scheduled timeout in %0.3fms", duration * 0.000001f); +#endif +} + +void VibratorInputMapper::stopVibrating() { + mVibrating = false; +#if DEBUG_VIBRATOR + ALOGD("stopVibrating: sending cancel vibrate deviceId=%d", getDeviceId()); +#endif + getEventHub()->cancelVibrate(getDeviceId()); +} + +void VibratorInputMapper::dump(String8& dump) { + dump.append(INDENT2 "Vibrator Input Mapper:\n"); + dump.appendFormat(INDENT3 "Vibrating: %s\n", toString(mVibrating)); +} + + // --- KeyboardInputMapper --- KeyboardInputMapper::KeyboardInputMapper(InputDevice* device, @@ -1775,7 +1982,7 @@ void KeyboardInputMapper::populateDeviceInfo(InputDeviceInfo* info) { InputMapper::populateDeviceInfo(info); info->setKeyboardType(mKeyboardType); - info->setKeyCharacterMapFile(getEventHub()->getKeyCharacterMapFile(getDeviceId())); + info->setKeyCharacterMap(getEventHub()->getKeyCharacterMap(getDeviceId())); } void KeyboardInputMapper::dump(String8& dump) { @@ -1799,9 +2006,11 @@ void KeyboardInputMapper::configure(nsecs_t when, } if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) { - if (mParameters.orientationAware && mParameters.associatedDisplayId >= 0) { - if (!config->getDisplayInfo(mParameters.associatedDisplayId, - false /*external*/, NULL, NULL, &mOrientation)) { + if (mParameters.orientationAware && mParameters.hasAssociatedDisplay) { + DisplayViewport v; + if (config->getDisplayInfo(false /*external*/, &v)) { + mOrientation = v.orientation; + } else { mOrientation = DISPLAY_ORIENTATION_0; } } else { @@ -1815,16 +2024,16 @@ void KeyboardInputMapper::configureParameters() { getDevice()->getConfiguration().tryGetProperty(String8("keyboard.orientationAware"), mParameters.orientationAware); - mParameters.associatedDisplayId = -1; + mParameters.hasAssociatedDisplay = false; if (mParameters.orientationAware) { - mParameters.associatedDisplayId = 0; + mParameters.hasAssociatedDisplay = true; } } void KeyboardInputMapper::dumpParameters(String8& dump) { dump.append(INDENT3 "Parameters:\n"); - dump.appendFormat(INDENT4 "AssociatedDisplayId: %d\n", - mParameters.associatedDisplayId); + dump.appendFormat(INDENT4 "HasAssociatedDisplay: %s\n", + toString(mParameters.hasAssociatedDisplay)); dump.appendFormat(INDENT4 "OrientationAware: %s\n", toString(mParameters.orientationAware)); } @@ -1833,6 +2042,7 @@ void KeyboardInputMapper::reset(nsecs_t when) { mMetaState = AMETA_NONE; mDownTime = 0; mKeyDowns.clear(); + mCurrentHidUsage = 0; resetLedState(); @@ -1842,13 +2052,32 @@ void KeyboardInputMapper::reset(nsecs_t when) { void KeyboardInputMapper::process(const RawEvent* rawEvent) { switch (rawEvent->type) { case EV_KEY: { - int32_t scanCode = rawEvent->scanCode; + int32_t scanCode = rawEvent->code; + int32_t usageCode = mCurrentHidUsage; + mCurrentHidUsage = 0; + if (isKeyboardOrGamepadKey(scanCode)) { - processKey(rawEvent->when, rawEvent->value != 0, rawEvent->keyCode, scanCode, - rawEvent->flags); + int32_t keyCode; + uint32_t flags; + if (getEventHub()->mapKey(getDeviceId(), scanCode, usageCode, &keyCode, &flags)) { + keyCode = AKEYCODE_UNKNOWN; + flags = 0; + } + processKey(rawEvent->when, rawEvent->value != 0, keyCode, scanCode, flags); } break; } + case EV_MSC: { + if (rawEvent->code == MSC_SCAN) { + mCurrentHidUsage = rawEvent->value; + } + break; + } + case EV_SYN: { + if (rawEvent->code == SYN_REPORT) { + mCurrentHidUsage = 0; + } + } } } @@ -1864,7 +2093,7 @@ void KeyboardInputMapper::processKey(nsecs_t when, bool down, int32_t keyCode, if (down) { // Rotate key codes according to orientation if needed. - if (mParameters.orientationAware && mParameters.associatedDisplayId >= 0) { + if (mParameters.orientationAware && mParameters.hasAssociatedDisplay) { keyCode = rotateKeyCode(keyCode, mOrientation); } @@ -2019,20 +2248,20 @@ void CursorInputMapper::populateDeviceInfo(InputDeviceInfo* info) { if (mParameters.mode == Parameters::MODE_POINTER) { float minX, minY, maxX, maxY; if (mPointerController->getBounds(&minX, &minY, &maxX, &maxY)) { - info->addMotionRange(AMOTION_EVENT_AXIS_X, mSource, minX, maxX, 0.0f, 0.0f); - info->addMotionRange(AMOTION_EVENT_AXIS_Y, mSource, minY, maxY, 0.0f, 0.0f); + info->addMotionRange(AMOTION_EVENT_AXIS_X, mSource, minX, maxX, 0.0f, 0.0f, 0.0f); + info->addMotionRange(AMOTION_EVENT_AXIS_Y, mSource, minY, maxY, 0.0f, 0.0f, 0.0f); } } else { - info->addMotionRange(AMOTION_EVENT_AXIS_X, mSource, -1.0f, 1.0f, 0.0f, mXScale); - info->addMotionRange(AMOTION_EVENT_AXIS_Y, mSource, -1.0f, 1.0f, 0.0f, mYScale); + info->addMotionRange(AMOTION_EVENT_AXIS_X, mSource, -1.0f, 1.0f, 0.0f, mXScale, 0.0f); + info->addMotionRange(AMOTION_EVENT_AXIS_Y, mSource, -1.0f, 1.0f, 0.0f, mYScale, 0.0f); } - info->addMotionRange(AMOTION_EVENT_AXIS_PRESSURE, mSource, 0.0f, 1.0f, 0.0f, 0.0f); + info->addMotionRange(AMOTION_EVENT_AXIS_PRESSURE, mSource, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f); if (mCursorScrollAccumulator.haveRelativeVWheel()) { - info->addMotionRange(AMOTION_EVENT_AXIS_VSCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f); + info->addMotionRange(AMOTION_EVENT_AXIS_VSCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f); } if (mCursorScrollAccumulator.haveRelativeHWheel()) { - info->addMotionRange(AMOTION_EVENT_AXIS_HSCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f); + info->addMotionRange(AMOTION_EVENT_AXIS_HSCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f); } } @@ -2095,14 +2324,17 @@ void CursorInputMapper::configure(nsecs_t when, } if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) { - if (mParameters.orientationAware && mParameters.associatedDisplayId >= 0) { - if (!config->getDisplayInfo(mParameters.associatedDisplayId, - false /*external*/, NULL, NULL, &mOrientation)) { + if (mParameters.orientationAware && mParameters.hasAssociatedDisplay) { + DisplayViewport v; + if (config->getDisplayInfo(false /*external*/, &v)) { + mOrientation = v.orientation; + } else { mOrientation = DISPLAY_ORIENTATION_0; } } else { mOrientation = DISPLAY_ORIENTATION_0; } + bumpGeneration(); } } @@ -2121,16 +2353,16 @@ void CursorInputMapper::configureParameters() { getDevice()->getConfiguration().tryGetProperty(String8("cursor.orientationAware"), mParameters.orientationAware); - mParameters.associatedDisplayId = -1; + mParameters.hasAssociatedDisplay = false; if (mParameters.mode == Parameters::MODE_POINTER || mParameters.orientationAware) { - mParameters.associatedDisplayId = 0; + mParameters.hasAssociatedDisplay = true; } } void CursorInputMapper::dumpParameters(String8& dump) { dump.append(INDENT3 "Parameters:\n"); - dump.appendFormat(INDENT4 "AssociatedDisplayId: %d\n", - mParameters.associatedDisplayId); + dump.appendFormat(INDENT4 "HasAssociatedDisplay: %s\n", + toString(mParameters.hasAssociatedDisplay)); switch (mParameters.mode) { case Parameters::MODE_POINTER: @@ -2167,7 +2399,7 @@ void CursorInputMapper::process(const RawEvent* rawEvent) { mCursorMotionAccumulator.process(rawEvent); mCursorScrollAccumulator.process(rawEvent); - if (rawEvent->type == EV_SYN && rawEvent->scanCode == SYN_REPORT) { + if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) { sync(rawEvent->when); } } @@ -2197,7 +2429,7 @@ void CursorInputMapper::sync(nsecs_t when) { bool moved = deltaX != 0 || deltaY != 0; // Rotate delta according to orientation if needed. - if (mParameters.orientationAware && mParameters.associatedDisplayId >= 0 + if (mParameters.orientationAware && mParameters.hasAssociatedDisplay && (deltaX != 0.0f || deltaY != 0.0f)) { rotateDelta(mOrientation, &deltaX, &deltaY); } @@ -2220,6 +2452,7 @@ void CursorInputMapper::sync(nsecs_t when) { mPointerVelocityControl.move(when, &deltaX, &deltaY); + int32_t displayId; if (mPointerController != NULL) { if (moved || scrolled || buttonsChanged) { mPointerController->setPresentation( @@ -2240,9 +2473,11 @@ void CursorInputMapper::sync(nsecs_t when) { mPointerController->getPosition(&x, &y); pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, x); pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, y); + displayId = ADISPLAY_ID_DEFAULT; } else { pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, deltaX); pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, deltaY); + displayId = ADISPLAY_ID_NONE; } pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, down ? 1.0f : 0.0f); @@ -2274,7 +2509,8 @@ void CursorInputMapper::sync(nsecs_t when) { NotifyMotionArgs args(when, getDeviceId(), mSource, policyFlags, motionEventAction, 0, metaState, currentButtonState, 0, - 1, &pointerProperties, &pointerCoords, mXPrecision, mYPrecision, downTime); + displayId, 1, &pointerProperties, &pointerCoords, + mXPrecision, mYPrecision, downTime); getListener()->notifyMotion(&args); // Send hover move after UP to tell the application that the mouse is hovering now. @@ -2283,7 +2519,8 @@ void CursorInputMapper::sync(nsecs_t when) { NotifyMotionArgs hoverArgs(when, getDeviceId(), mSource, policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, metaState, currentButtonState, AMOTION_EVENT_EDGE_FLAG_NONE, - 1, &pointerProperties, &pointerCoords, mXPrecision, mYPrecision, downTime); + displayId, 1, &pointerProperties, &pointerCoords, + mXPrecision, mYPrecision, downTime); getListener()->notifyMotion(&hoverArgs); } @@ -2295,7 +2532,8 @@ void CursorInputMapper::sync(nsecs_t when) { NotifyMotionArgs scrollArgs(when, getDeviceId(), mSource, policyFlags, AMOTION_EVENT_ACTION_SCROLL, 0, metaState, currentButtonState, AMOTION_EVENT_EDGE_FLAG_NONE, - 1, &pointerProperties, &pointerCoords, mXPrecision, mYPrecision, downTime); + displayId, 1, &pointerProperties, &pointerCoords, + mXPrecision, mYPrecision, downTime); getListener()->notifyMotion(&scrollArgs); } } @@ -2328,8 +2566,8 @@ void CursorInputMapper::fadePointer() { TouchInputMapper::TouchInputMapper(InputDevice* device) : InputMapper(device), mSource(0), mDeviceMode(DEVICE_MODE_DISABLED), - mSurfaceOrientation(-1), mSurfaceWidth(-1), mSurfaceHeight(-1), - mPointerUsage(POINTER_USAGE_NONE) { + mSurfaceWidth(-1), mSurfaceHeight(-1), mSurfaceLeft(0), mSurfaceTop(0), + mSurfaceOrientation(DISPLAY_ORIENTATION_0) { } TouchInputMapper::~TouchInputMapper() { @@ -2374,10 +2612,24 @@ void TouchInputMapper::populateDeviceInfo(InputDeviceInfo* info) { } if (mCursorScrollAccumulator.haveRelativeVWheel()) { - info->addMotionRange(AMOTION_EVENT_AXIS_VSCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f); + info->addMotionRange(AMOTION_EVENT_AXIS_VSCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f, + 0.0f); } if (mCursorScrollAccumulator.haveRelativeHWheel()) { - info->addMotionRange(AMOTION_EVENT_AXIS_HSCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f); + info->addMotionRange(AMOTION_EVENT_AXIS_HSCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f, + 0.0f); + } + if (mCalibration.coverageCalibration == Calibration::COVERAGE_CALIBRATION_BOX) { + const InputDeviceInfo::MotionRange& x = mOrientedRanges.x; + const InputDeviceInfo::MotionRange& y = mOrientedRanges.y; + info->addMotionRange(AMOTION_EVENT_AXIS_GENERIC_1, mSource, x.min, x.max, x.flat, + x.fuzz, x.resolution); + info->addMotionRange(AMOTION_EVENT_AXIS_GENERIC_2, mSource, y.min, y.max, y.flat, + y.fuzz, y.resolution); + info->addMotionRange(AMOTION_EVENT_AXIS_GENERIC_3, mSource, x.min, x.max, x.flat, + x.fuzz, x.resolution); + info->addMotionRange(AMOTION_EVENT_AXIS_GENERIC_4, mSource, y.min, y.max, y.flat, + y.fuzz, y.resolution); } } } @@ -2391,6 +2643,8 @@ void TouchInputMapper::dump(String8& dump) { dumpSurface(dump); dump.appendFormat(INDENT3 "Translation and Scaling Factors:\n"); + dump.appendFormat(INDENT4 "XTranslate: %0.3f\n", mXTranslate); + dump.appendFormat(INDENT4 "YTranslate: %0.3f\n", mYTranslate); dump.appendFormat(INDENT4 "XScale: %0.3f\n", mXScale); dump.appendFormat(INDENT4 "YScale: %0.3f\n", mYScale); dump.appendFormat(INDENT4 "XPrecision: %0.3f\n", mXPrecision); @@ -2398,7 +2652,6 @@ void TouchInputMapper::dump(String8& dump) { dump.appendFormat(INDENT4 "GeometricScale: %0.3f\n", mGeometricScale); dump.appendFormat(INDENT4 "PressureScale: %0.3f\n", mPressureScale); dump.appendFormat(INDENT4 "SizeScale: %0.3f\n", mSizeScale); - dump.appendFormat(INDENT4 "OrientationCenter: %0.3f\n", mOrientationCenter); dump.appendFormat(INDENT4 "OrientationScale: %0.3f\n", mOrientationScale); dump.appendFormat(INDENT4 "DistanceScale: %0.3f\n", mDistanceScale); dump.appendFormat(INDENT4 "HaveTilt: %s\n", toString(mHaveTilt)); @@ -2550,6 +2803,8 @@ void TouchInputMapper::configureParameters() { mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_SCREEN; } else if (deviceTypeString == "touchPad") { mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_PAD; + } else if (deviceTypeString == "touchNavigation") { + mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_NAVIGATION; } else if (deviceTypeString == "pointer") { mParameters.deviceType = Parameters::DEVICE_TYPE_POINTER; } else if (deviceTypeString != "default") { @@ -2561,15 +2816,15 @@ void TouchInputMapper::configureParameters() { getDevice()->getConfiguration().tryGetProperty(String8("touch.orientationAware"), mParameters.orientationAware); - mParameters.associatedDisplayId = -1; + mParameters.hasAssociatedDisplay = false; mParameters.associatedDisplayIsExternal = false; if (mParameters.orientationAware || mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_SCREEN || mParameters.deviceType == Parameters::DEVICE_TYPE_POINTER) { + mParameters.hasAssociatedDisplay = true; mParameters.associatedDisplayIsExternal = mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_SCREEN && getDevice()->isExternal(); - mParameters.associatedDisplayId = 0; } } @@ -2594,6 +2849,9 @@ void TouchInputMapper::dumpParameters(String8& dump) { case Parameters::DEVICE_TYPE_TOUCH_PAD: dump.append(INDENT4 "DeviceType: touchPad\n"); break; + case Parameters::DEVICE_TYPE_TOUCH_NAVIGATION: + dump.append(INDENT4 "DeviceType: touchNavigation\n"); + break; case Parameters::DEVICE_TYPE_POINTER: dump.append(INDENT4 "DeviceType: pointer\n"); break; @@ -2601,8 +2859,9 @@ void TouchInputMapper::dumpParameters(String8& dump) { ALOG_ASSERT(false); } - dump.appendFormat(INDENT4 "AssociatedDisplay: id=%d, isExternal=%s\n", - mParameters.associatedDisplayId, toString(mParameters.associatedDisplayIsExternal)); + dump.appendFormat(INDENT4 "AssociatedDisplay: hasAssociatedDisplay=%s, isExternal=%s\n", + toString(mParameters.hasAssociatedDisplay), + toString(mParameters.associatedDisplayIsExternal)); dump.appendFormat(INDENT4 "OrientationAware: %s\n", toString(mParameters.orientationAware)); } @@ -2636,10 +2895,19 @@ void TouchInputMapper::configureSurface(nsecs_t when, bool* outResetNeeded) { && mConfig.pointerGesturesEnabled) { mSource = AINPUT_SOURCE_MOUSE; mDeviceMode = DEVICE_MODE_POINTER; + if (hasStylus()) { + mSource |= AINPUT_SOURCE_STYLUS; + } } else if (mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_SCREEN - && mParameters.associatedDisplayId >= 0) { + && mParameters.hasAssociatedDisplay) { mSource = AINPUT_SOURCE_TOUCHSCREEN; mDeviceMode = DEVICE_MODE_DIRECT; + if (hasStylus()) { + mSource |= AINPUT_SOURCE_STYLUS; + } + } else if (mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_NAVIGATION) { + mSource = AINPUT_SOURCE_TOUCH_NAVIGATION; + mDeviceMode = DEVICE_MODE_NAVIGATION; } else { mSource = AINPUT_SOURCE_TOUCHPAD; mDeviceMode = DEVICE_MODE_UNSCALED; @@ -2653,32 +2921,93 @@ void TouchInputMapper::configureSurface(nsecs_t when, bool* outResetNeeded) { return; } + // Raw width and height in the natural orientation. + int32_t rawWidth = mRawPointerAxes.x.maxValue - mRawPointerAxes.x.minValue + 1; + int32_t rawHeight = mRawPointerAxes.y.maxValue - mRawPointerAxes.y.minValue + 1; + // Get associated display dimensions. - if (mParameters.associatedDisplayId >= 0) { - if (!mConfig.getDisplayInfo(mParameters.associatedDisplayId, - mParameters.associatedDisplayIsExternal, - &mAssociatedDisplayWidth, &mAssociatedDisplayHeight, - &mAssociatedDisplayOrientation)) { + bool viewportChanged = false; + DisplayViewport newViewport; + if (mParameters.hasAssociatedDisplay) { + if (!mConfig.getDisplayInfo(mParameters.associatedDisplayIsExternal, &newViewport)) { ALOGI(INDENT "Touch device '%s' could not query the properties of its associated " - "display %d. The device will be inoperable until the display size " + "display. The device will be inoperable until the display size " "becomes available.", - getDeviceName().string(), mParameters.associatedDisplayId); + getDeviceName().string()); mDeviceMode = DEVICE_MODE_DISABLED; return; } - } - - // Configure dimensions. - int32_t width, height, orientation; - if (mDeviceMode == DEVICE_MODE_DIRECT || mDeviceMode == DEVICE_MODE_POINTER) { - width = mAssociatedDisplayWidth; - height = mAssociatedDisplayHeight; - orientation = mParameters.orientationAware ? - mAssociatedDisplayOrientation : DISPLAY_ORIENTATION_0; } else { - width = mRawPointerAxes.x.maxValue - mRawPointerAxes.x.minValue + 1; - height = mRawPointerAxes.y.maxValue - mRawPointerAxes.y.minValue + 1; - orientation = DISPLAY_ORIENTATION_0; + newViewport.setNonDisplayViewport(rawWidth, rawHeight); + } + if (mViewport != newViewport) { + mViewport = newViewport; + viewportChanged = true; + + if (mDeviceMode == DEVICE_MODE_DIRECT || mDeviceMode == DEVICE_MODE_POINTER) { + // Convert rotated viewport to natural surface coordinates. + int32_t naturalLogicalWidth, naturalLogicalHeight; + int32_t naturalPhysicalWidth, naturalPhysicalHeight; + int32_t naturalPhysicalLeft, naturalPhysicalTop; + int32_t naturalDeviceWidth, naturalDeviceHeight; + switch (mViewport.orientation) { + case DISPLAY_ORIENTATION_90: + naturalLogicalWidth = mViewport.logicalBottom - mViewport.logicalTop; + naturalLogicalHeight = mViewport.logicalRight - mViewport.logicalLeft; + naturalPhysicalWidth = mViewport.physicalBottom - mViewport.physicalTop; + naturalPhysicalHeight = mViewport.physicalRight - mViewport.physicalLeft; + naturalPhysicalLeft = mViewport.deviceHeight - mViewport.physicalBottom; + naturalPhysicalTop = mViewport.physicalLeft; + naturalDeviceWidth = mViewport.deviceHeight; + naturalDeviceHeight = mViewport.deviceWidth; + break; + case DISPLAY_ORIENTATION_180: + naturalLogicalWidth = mViewport.logicalRight - mViewport.logicalLeft; + naturalLogicalHeight = mViewport.logicalBottom - mViewport.logicalTop; + naturalPhysicalWidth = mViewport.physicalRight - mViewport.physicalLeft; + naturalPhysicalHeight = mViewport.physicalBottom - mViewport.physicalTop; + naturalPhysicalLeft = mViewport.deviceWidth - mViewport.physicalRight; + naturalPhysicalTop = mViewport.deviceHeight - mViewport.physicalBottom; + naturalDeviceWidth = mViewport.deviceWidth; + naturalDeviceHeight = mViewport.deviceHeight; + break; + case DISPLAY_ORIENTATION_270: + naturalLogicalWidth = mViewport.logicalBottom - mViewport.logicalTop; + naturalLogicalHeight = mViewport.logicalRight - mViewport.logicalLeft; + naturalPhysicalWidth = mViewport.physicalBottom - mViewport.physicalTop; + naturalPhysicalHeight = mViewport.physicalRight - mViewport.physicalLeft; + naturalPhysicalLeft = mViewport.physicalTop; + naturalPhysicalTop = mViewport.deviceWidth - mViewport.physicalRight; + naturalDeviceWidth = mViewport.deviceHeight; + naturalDeviceHeight = mViewport.deviceWidth; + break; + case DISPLAY_ORIENTATION_0: + default: + naturalLogicalWidth = mViewport.logicalRight - mViewport.logicalLeft; + naturalLogicalHeight = mViewport.logicalBottom - mViewport.logicalTop; + naturalPhysicalWidth = mViewport.physicalRight - mViewport.physicalLeft; + naturalPhysicalHeight = mViewport.physicalBottom - mViewport.physicalTop; + naturalPhysicalLeft = mViewport.physicalLeft; + naturalPhysicalTop = mViewport.physicalTop; + naturalDeviceWidth = mViewport.deviceWidth; + naturalDeviceHeight = mViewport.deviceHeight; + break; + } + + mSurfaceWidth = naturalLogicalWidth * naturalDeviceWidth / naturalPhysicalWidth; + mSurfaceHeight = naturalLogicalHeight * naturalDeviceHeight / naturalPhysicalHeight; + mSurfaceLeft = naturalPhysicalLeft * naturalLogicalWidth / naturalPhysicalWidth; + mSurfaceTop = naturalPhysicalTop * naturalLogicalHeight / naturalPhysicalHeight; + + mSurfaceOrientation = mParameters.orientationAware ? + mViewport.orientation : DISPLAY_ORIENTATION_0; + } else { + mSurfaceWidth = rawWidth; + mSurfaceHeight = rawHeight; + mSurfaceLeft = 0; + mSurfaceTop = 0; + mSurfaceOrientation = DISPLAY_ORIENTATION_0; + } } // If moving between pointer modes, need to reset some state. @@ -2698,22 +3027,17 @@ void TouchInputMapper::configureSurface(nsecs_t when, bool* outResetNeeded) { mPointerController.clear(); } - bool orientationChanged = mSurfaceOrientation != orientation; - if (orientationChanged) { - mSurfaceOrientation = orientation; - } - - bool sizeChanged = mSurfaceWidth != width || mSurfaceHeight != height; - if (sizeChanged || deviceModeChanged) { - ALOGI("Device reconfigured: id=%d, name='%s', surface size is now %dx%d, mode is %d", - getDeviceId(), getDeviceName().string(), width, height, mDeviceMode); - - mSurfaceWidth = width; - mSurfaceHeight = height; + if (viewportChanged || deviceModeChanged) { + ALOGI("Device reconfigured: id=%d, name='%s', size %dx%d, orientation %d, mode %d, " + "display id %d", + getDeviceId(), getDeviceName().string(), mSurfaceWidth, mSurfaceHeight, + mSurfaceOrientation, mDeviceMode, mViewport.displayId); // Configure X and Y factors. - mXScale = float(width) / (mRawPointerAxes.x.maxValue - mRawPointerAxes.x.minValue + 1); - mYScale = float(height) / (mRawPointerAxes.y.maxValue - mRawPointerAxes.y.minValue + 1); + mXScale = float(mSurfaceWidth) / rawWidth; + mYScale = float(mSurfaceHeight) / rawHeight; + mXTranslate = -mSurfaceLeft; + mYTranslate = -mSurfaceTop; mXPrecision = 1.0f / mXScale; mYPrecision = 1.0f / mYScale; @@ -2730,7 +3054,7 @@ void TouchInputMapper::configureSurface(nsecs_t when, bool* outResetNeeded) { mGeometricScale = avg(mXScale, mYScale); // Size of diagonal axis. - float diagonalSize = hypotf(width, height); + float diagonalSize = hypotf(mSurfaceWidth, mSurfaceHeight); // Size factors. if (mCalibration.sizeCalibration != Calibration::SIZE_CALIBRATION_NONE) { @@ -2754,6 +3078,7 @@ void TouchInputMapper::configureSurface(nsecs_t when, bool* outResetNeeded) { mOrientedRanges.touchMajor.max = diagonalSize; mOrientedRanges.touchMajor.flat = 0; mOrientedRanges.touchMajor.fuzz = 0; + mOrientedRanges.touchMajor.resolution = 0; mOrientedRanges.touchMinor = mOrientedRanges.touchMajor; mOrientedRanges.touchMinor.axis = AMOTION_EVENT_AXIS_TOUCH_MINOR; @@ -2764,6 +3089,7 @@ void TouchInputMapper::configureSurface(nsecs_t when, bool* outResetNeeded) { mOrientedRanges.toolMajor.max = diagonalSize; mOrientedRanges.toolMajor.flat = 0; mOrientedRanges.toolMajor.fuzz = 0; + mOrientedRanges.toolMajor.resolution = 0; mOrientedRanges.toolMinor = mOrientedRanges.toolMajor; mOrientedRanges.toolMinor.axis = AMOTION_EVENT_AXIS_TOOL_MINOR; @@ -2774,6 +3100,7 @@ void TouchInputMapper::configureSurface(nsecs_t when, bool* outResetNeeded) { mOrientedRanges.size.max = 1.0; mOrientedRanges.size.flat = 0; mOrientedRanges.size.fuzz = 0; + mOrientedRanges.size.resolution = 0; } else { mSizeScale = 0.0f; } @@ -2797,6 +3124,7 @@ void TouchInputMapper::configureSurface(nsecs_t when, bool* outResetNeeded) { mOrientedRanges.pressure.max = 1.0; mOrientedRanges.pressure.flat = 0; mOrientedRanges.pressure.fuzz = 0; + mOrientedRanges.pressure.resolution = 0; // Tilt mTiltXCenter = 0; @@ -2820,10 +3148,10 @@ void TouchInputMapper::configureSurface(nsecs_t when, bool* outResetNeeded) { mOrientedRanges.tilt.max = M_PI_2; mOrientedRanges.tilt.flat = 0; mOrientedRanges.tilt.fuzz = 0; + mOrientedRanges.tilt.resolution = 0; } // Orientation - mOrientationCenter = 0; mOrientationScale = 0; if (mHaveTilt) { mOrientedRanges.haveOrientation = true; @@ -2834,15 +3162,19 @@ void TouchInputMapper::configureSurface(nsecs_t when, bool* outResetNeeded) { mOrientedRanges.orientation.max = M_PI; mOrientedRanges.orientation.flat = 0; mOrientedRanges.orientation.fuzz = 0; + mOrientedRanges.orientation.resolution = 0; } else if (mCalibration.orientationCalibration != Calibration::ORIENTATION_CALIBRATION_NONE) { if (mCalibration.orientationCalibration == Calibration::ORIENTATION_CALIBRATION_INTERPOLATED) { if (mRawPointerAxes.orientation.valid) { - mOrientationCenter = avg(mRawPointerAxes.orientation.minValue, - mRawPointerAxes.orientation.maxValue); - mOrientationScale = M_PI / (mRawPointerAxes.orientation.maxValue - - mRawPointerAxes.orientation.minValue); + if (mRawPointerAxes.orientation.maxValue > 0) { + mOrientationScale = M_PI_2 / mRawPointerAxes.orientation.maxValue; + } else if (mRawPointerAxes.orientation.minValue < 0) { + mOrientationScale = -M_PI_2 / mRawPointerAxes.orientation.minValue; + } else { + mOrientationScale = 0; + } } } @@ -2854,6 +3186,7 @@ void TouchInputMapper::configureSurface(nsecs_t when, bool* outResetNeeded) { mOrientedRanges.orientation.max = M_PI_2; mOrientedRanges.orientation.flat = 0; mOrientedRanges.orientation.fuzz = 0; + mOrientedRanges.orientation.resolution = 0; } // Distance @@ -2875,67 +3208,57 @@ void TouchInputMapper::configureSurface(nsecs_t when, bool* outResetNeeded) { mOrientedRanges.distance.min = mRawPointerAxes.distance.minValue * mDistanceScale; mOrientedRanges.distance.max = - mRawPointerAxes.distance.minValue * mDistanceScale; + mRawPointerAxes.distance.maxValue * mDistanceScale; mOrientedRanges.distance.flat = 0; mOrientedRanges.distance.fuzz = mRawPointerAxes.distance.fuzz * mDistanceScale; + mOrientedRanges.distance.resolution = 0; } - } - if (orientationChanged || sizeChanged || deviceModeChanged) { - // Compute oriented surface dimensions, precision, scales and ranges. + // Compute oriented precision, scales and ranges. // Note that the maximum value reported is an inclusive maximum value so it is one // unit less than the total width or height of surface. switch (mSurfaceOrientation) { case DISPLAY_ORIENTATION_90: case DISPLAY_ORIENTATION_270: - mOrientedSurfaceWidth = mSurfaceHeight; - mOrientedSurfaceHeight = mSurfaceWidth; - mOrientedXPrecision = mYPrecision; mOrientedYPrecision = mXPrecision; - mOrientedRanges.x.min = 0; - mOrientedRanges.x.max = (mRawPointerAxes.y.maxValue - mRawPointerAxes.y.minValue) - * mYScale; + mOrientedRanges.x.min = mYTranslate; + mOrientedRanges.x.max = mSurfaceHeight + mYTranslate - 1; mOrientedRanges.x.flat = 0; - mOrientedRanges.x.fuzz = mYScale; + mOrientedRanges.x.fuzz = 0; + mOrientedRanges.x.resolution = mRawPointerAxes.y.resolution * mYScale; - mOrientedRanges.y.min = 0; - mOrientedRanges.y.max = (mRawPointerAxes.x.maxValue - mRawPointerAxes.x.minValue) - * mXScale; + mOrientedRanges.y.min = mXTranslate; + mOrientedRanges.y.max = mSurfaceWidth + mXTranslate - 1; mOrientedRanges.y.flat = 0; - mOrientedRanges.y.fuzz = mXScale; + mOrientedRanges.y.fuzz = 0; + mOrientedRanges.y.resolution = mRawPointerAxes.x.resolution * mXScale; break; default: - mOrientedSurfaceWidth = mSurfaceWidth; - mOrientedSurfaceHeight = mSurfaceHeight; - mOrientedXPrecision = mXPrecision; mOrientedYPrecision = mYPrecision; - mOrientedRanges.x.min = 0; - mOrientedRanges.x.max = (mRawPointerAxes.x.maxValue - mRawPointerAxes.x.minValue) - * mXScale; + mOrientedRanges.x.min = mXTranslate; + mOrientedRanges.x.max = mSurfaceWidth + mXTranslate - 1; mOrientedRanges.x.flat = 0; - mOrientedRanges.x.fuzz = mXScale; + mOrientedRanges.x.fuzz = 0; + mOrientedRanges.x.resolution = mRawPointerAxes.x.resolution * mXScale; - mOrientedRanges.y.min = 0; - mOrientedRanges.y.max = (mRawPointerAxes.y.maxValue - mRawPointerAxes.y.minValue) - * mYScale; + mOrientedRanges.y.min = mYTranslate; + mOrientedRanges.y.max = mSurfaceHeight + mYTranslate - 1; mOrientedRanges.y.flat = 0; - mOrientedRanges.y.fuzz = mYScale; + mOrientedRanges.y.fuzz = 0; + mOrientedRanges.y.resolution = mRawPointerAxes.y.resolution * mYScale; break; } - // Compute pointer gesture detection parameters. if (mDeviceMode == DEVICE_MODE_POINTER) { - int32_t rawWidth = mRawPointerAxes.x.maxValue - mRawPointerAxes.x.minValue + 1; - int32_t rawHeight = mRawPointerAxes.y.maxValue - mRawPointerAxes.y.minValue + 1; + // Compute pointer gesture detection parameters. float rawDiagonal = hypotf(rawWidth, rawHeight); - float displayDiagonal = hypotf(mAssociatedDisplayWidth, - mAssociatedDisplayHeight); + float displayDiagonal = hypotf(mSurfaceWidth, mSurfaceHeight); // Scale movements such that one whole swipe of the touch pad covers a // given area relative to the diagonal size of the display when no acceleration @@ -2958,19 +3281,33 @@ void TouchInputMapper::configureSurface(nsecs_t when, bool* outResetNeeded) { // translated into freeform gestures. mPointerGestureMaxSwipeWidth = mConfig.pointerGestureSwipeMaxWidthRatio * rawDiagonal; - } - // Abort current pointer usages because the state has changed. - abortPointerUsage(when, 0 /*policyFlags*/); + // Abort current pointer usages because the state has changed. + abortPointerUsage(when, 0 /*policyFlags*/); + } // Inform the dispatcher about the changes. *outResetNeeded = true; + bumpGeneration(); } } void TouchInputMapper::dumpSurface(String8& dump) { + dump.appendFormat(INDENT3 "Viewport: displayId=%d, orientation=%d, " + "logicalFrame=[%d, %d, %d, %d], " + "physicalFrame=[%d, %d, %d, %d], " + "deviceSize=[%d, %d]\n", + mViewport.displayId, mViewport.orientation, + mViewport.logicalLeft, mViewport.logicalTop, + mViewport.logicalRight, mViewport.logicalBottom, + mViewport.physicalLeft, mViewport.physicalTop, + mViewport.physicalRight, mViewport.physicalBottom, + mViewport.deviceWidth, mViewport.deviceHeight); + dump.appendFormat(INDENT3 "SurfaceWidth: %dpx\n", mSurfaceWidth); dump.appendFormat(INDENT3 "SurfaceHeight: %dpx\n", mSurfaceHeight); + dump.appendFormat(INDENT3 "SurfaceLeft: %d\n", mSurfaceLeft); + dump.appendFormat(INDENT3 "SurfaceTop: %d\n", mSurfaceTop); dump.appendFormat(INDENT3 "SurfaceOrientation: %d\n", mSurfaceOrientation); } @@ -3001,8 +3338,7 @@ void TouchInputMapper::configureVirtualKeys() { virtualKey.scanCode = virtualKeyDefinition.scanCode; int32_t keyCode; uint32_t flags; - if (getEventHub()->mapKey(getDeviceId(), virtualKey.scanCode, - & keyCode, & flags)) { + if (getEventHub()->mapKey(getDeviceId(), virtualKey.scanCode, 0, &keyCode, &flags)) { ALOGW(INDENT "VirtualKey %d: could not obtain key code, ignoring", virtualKey.scanCode); mVirtualKeys.pop(); // drop the key @@ -3056,6 +3392,8 @@ void TouchInputMapper::parseCalibration() { out.sizeCalibration = Calibration::SIZE_CALIBRATION_GEOMETRIC; } else if (sizeCalibrationString == "diameter") { out.sizeCalibration = Calibration::SIZE_CALIBRATION_DIAMETER; + } else if (sizeCalibrationString == "box") { + out.sizeCalibration = Calibration::SIZE_CALIBRATION_BOX; } else if (sizeCalibrationString == "area") { out.sizeCalibration = Calibration::SIZE_CALIBRATION_AREA; } else if (sizeCalibrationString != "default") { @@ -3122,6 +3460,19 @@ void TouchInputMapper::parseCalibration() { out.haveDistanceScale = in.tryGetProperty(String8("touch.distance.scale"), out.distanceScale); + + out.coverageCalibration = Calibration::COVERAGE_CALIBRATION_DEFAULT; + String8 coverageCalibrationString; + if (in.tryGetProperty(String8("touch.coverage.calibration"), coverageCalibrationString)) { + if (coverageCalibrationString == "none") { + out.coverageCalibration = Calibration::COVERAGE_CALIBRATION_NONE; + } else if (coverageCalibrationString == "box") { + out.coverageCalibration = Calibration::COVERAGE_CALIBRATION_BOX; + } else if (coverageCalibrationString != "default") { + ALOGW("Invalid value for touch.coverage.calibration: '%s'", + coverageCalibrationString.string()); + } + } } void TouchInputMapper::resolveCalibration() { @@ -3160,6 +3511,11 @@ void TouchInputMapper::resolveCalibration() { } else { mCalibration.distanceCalibration = Calibration::DISTANCE_CALIBRATION_NONE; } + + // Coverage + if (mCalibration.coverageCalibration == Calibration::COVERAGE_CALIBRATION_DEFAULT) { + mCalibration.coverageCalibration = Calibration::COVERAGE_CALIBRATION_NONE; + } } void TouchInputMapper::dumpCalibration(String8& dump) { @@ -3176,6 +3532,9 @@ void TouchInputMapper::dumpCalibration(String8& dump) { case Calibration::SIZE_CALIBRATION_DIAMETER: dump.append(INDENT4 "touch.size.calibration: diameter\n"); break; + case Calibration::SIZE_CALIBRATION_BOX: + dump.append(INDENT4 "touch.size.calibration: box\n"); + break; case Calibration::SIZE_CALIBRATION_AREA: dump.append(INDENT4 "touch.size.calibration: area\n"); break; @@ -3249,6 +3608,17 @@ void TouchInputMapper::dumpCalibration(String8& dump) { dump.appendFormat(INDENT4 "touch.distance.scale: %0.3f\n", mCalibration.distanceScale); } + + switch (mCalibration.coverageCalibration) { + case Calibration::COVERAGE_CALIBRATION_NONE: + dump.append(INDENT4 "touch.coverage.calibration: none\n"); + break; + case Calibration::COVERAGE_CALIBRATION_BOX: + dump.append(INDENT4 "touch.coverage.calibration: box\n"); + break; + default: + ALOG_ASSERT(false); + } } void TouchInputMapper::reset(nsecs_t when) { @@ -3296,7 +3666,7 @@ void TouchInputMapper::process(const RawEvent* rawEvent) { mCursorScrollAccumulator.process(rawEvent); mTouchButtonAccumulator.process(rawEvent); - if (rawEvent->type == EV_SYN && rawEvent->scanCode == SYN_REPORT) { + if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) { sync(rawEvent->when); } } @@ -3726,6 +4096,7 @@ void TouchInputMapper::cookPointerData() { switch (mCalibration.sizeCalibration) { case Calibration::SIZE_CALIBRATION_GEOMETRIC: case Calibration::SIZE_CALIBRATION_DIAMETER: + case Calibration::SIZE_CALIBRATION_BOX: case Calibration::SIZE_CALIBRATION_AREA: if (mRawPointerAxes.touchMajor.valid && mRawPointerAxes.toolMajor.valid) { touchMajor = in.touchMajor; @@ -3822,7 +4193,7 @@ void TouchInputMapper::cookPointerData() { switch (mCalibration.orientationCalibration) { case Calibration::ORIENTATION_CALIBRATION_INTERPOLATED: - orientation = (in.orientation - mOrientationCenter) * mOrientationScale; + orientation = in.orientation * mOrientationScale; break; case Calibration::ORIENTATION_CALIBRATION_VECTOR: { int32_t c1 = signExtendNybble((in.orientation & 0xf0) >> 4); @@ -3855,33 +4226,63 @@ void TouchInputMapper::cookPointerData() { distance = 0; } - // X and Y + // Coverage + int32_t rawLeft, rawTop, rawRight, rawBottom; + switch (mCalibration.coverageCalibration) { + case Calibration::COVERAGE_CALIBRATION_BOX: + rawLeft = (in.toolMinor & 0xffff0000) >> 16; + rawRight = in.toolMinor & 0x0000ffff; + rawBottom = in.toolMajor & 0x0000ffff; + rawTop = (in.toolMajor & 0xffff0000) >> 16; + break; + default: + rawLeft = rawTop = rawRight = rawBottom = 0; + break; + } + + // X, Y, and the bounding box for coverage information // Adjust coords for surface orientation. - float x, y; + float x, y, left, top, right, bottom; switch (mSurfaceOrientation) { case DISPLAY_ORIENTATION_90: - x = float(in.y - mRawPointerAxes.y.minValue) * mYScale; - y = float(mRawPointerAxes.x.maxValue - in.x) * mXScale; + x = float(in.y - mRawPointerAxes.y.minValue) * mYScale + mYTranslate; + y = float(mRawPointerAxes.x.maxValue - in.x) * mXScale + mXTranslate; + left = float(rawTop - mRawPointerAxes.y.minValue) * mYScale + mYTranslate; + right = float(rawBottom- mRawPointerAxes.y.minValue) * mYScale + mYTranslate; + bottom = float(mRawPointerAxes.x.maxValue - rawLeft) * mXScale + mXTranslate; + top = float(mRawPointerAxes.x.maxValue - rawRight) * mXScale + mXTranslate; orientation -= M_PI_2; if (orientation < - M_PI_2) { orientation += M_PI; } break; case DISPLAY_ORIENTATION_180: - x = float(mRawPointerAxes.x.maxValue - in.x) * mXScale; - y = float(mRawPointerAxes.y.maxValue - in.y) * mYScale; + x = float(mRawPointerAxes.x.maxValue - in.x) * mXScale + mXTranslate; + y = float(mRawPointerAxes.y.maxValue - in.y) * mYScale + mYTranslate; + left = float(mRawPointerAxes.x.maxValue - rawRight) * mXScale + mXTranslate; + right = float(mRawPointerAxes.x.maxValue - rawLeft) * mXScale + mXTranslate; + bottom = float(mRawPointerAxes.y.maxValue - rawTop) * mYScale + mYTranslate; + top = float(mRawPointerAxes.y.maxValue - rawBottom) * mYScale + mYTranslate; break; case DISPLAY_ORIENTATION_270: - x = float(mRawPointerAxes.y.maxValue - in.y) * mYScale; - y = float(in.x - mRawPointerAxes.x.minValue) * mXScale; + x = float(mRawPointerAxes.y.maxValue - in.y) * mYScale + mYTranslate; + y = float(in.x - mRawPointerAxes.x.minValue) * mXScale + mXTranslate; + left = float(mRawPointerAxes.y.maxValue - rawBottom) * mYScale + mYTranslate; + right = float(mRawPointerAxes.y.maxValue - rawTop) * mYScale + mYTranslate; + bottom = float(rawRight - mRawPointerAxes.x.minValue) * mXScale + mXTranslate; + top = float(rawLeft - mRawPointerAxes.x.minValue) * mXScale + mXTranslate; orientation += M_PI_2; if (orientation > M_PI_2) { orientation -= M_PI; } break; default: - x = float(in.x - mRawPointerAxes.x.minValue) * mXScale; - y = float(in.y - mRawPointerAxes.y.minValue) * mYScale; + x = float(in.x - mRawPointerAxes.x.minValue) * mXScale + mXTranslate; + y = float(in.y - mRawPointerAxes.y.minValue) * mYScale + mYTranslate; + left = float(rawLeft - mRawPointerAxes.x.minValue) * mXScale + mXTranslate; + right = float(rawRight - mRawPointerAxes.x.minValue) * mXScale + mXTranslate; + bottom = float(rawBottom - mRawPointerAxes.y.minValue) * mYScale + mYTranslate; + top = float(rawTop - mRawPointerAxes.y.minValue) * mYScale + mYTranslate; break; } @@ -3894,11 +4295,18 @@ void TouchInputMapper::cookPointerData() { out.setAxisValue(AMOTION_EVENT_AXIS_SIZE, size); out.setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, touchMajor); out.setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, touchMinor); - out.setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, toolMajor); - out.setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, toolMinor); out.setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, orientation); out.setAxisValue(AMOTION_EVENT_AXIS_TILT, tilt); out.setAxisValue(AMOTION_EVENT_AXIS_DISTANCE, distance); + if (mCalibration.coverageCalibration == Calibration::COVERAGE_CALIBRATION_BOX) { + out.setAxisValue(AMOTION_EVENT_AXIS_GENERIC_1, left); + out.setAxisValue(AMOTION_EVENT_AXIS_GENERIC_2, top); + out.setAxisValue(AMOTION_EVENT_AXIS_GENERIC_3, right); + out.setAxisValue(AMOTION_EVENT_AXIS_GENERIC_4, bottom); + } else { + out.setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, toolMajor); + out.setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, toolMinor); + } // Write output properties. PointerProperties& properties = mCurrentCookedPointerData.pointerProperties[i]; @@ -4137,7 +4545,8 @@ void TouchInputMapper::dispatchPointerGestures(nsecs_t when, uint32_t policyFlag NotifyMotionArgs args(when, getDeviceId(), mSource, policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE, - 1, &pointerProperties, &pointerCoords, 0, 0, mPointerGesture.downTime); + mViewport.displayId, 1, &pointerProperties, &pointerCoords, + 0, 0, mPointerGesture.downTime); getListener()->notifyMotion(&args); } @@ -5045,6 +5454,7 @@ void TouchInputMapper::dispatchPointerSimple(nsecs_t when, uint32_t policyFlags, // Send up. NotifyMotionArgs args(when, getDeviceId(), mSource, policyFlags, AMOTION_EVENT_ACTION_UP, 0, metaState, mLastButtonState, 0, + mViewport.displayId, 1, &mPointerSimple.lastProperties, &mPointerSimple.lastCoords, mOrientedXPrecision, mOrientedYPrecision, mPointerSimple.downTime); @@ -5057,6 +5467,7 @@ void TouchInputMapper::dispatchPointerSimple(nsecs_t when, uint32_t policyFlags, // Send hover exit. NotifyMotionArgs args(when, getDeviceId(), mSource, policyFlags, AMOTION_EVENT_ACTION_HOVER_EXIT, 0, metaState, mLastButtonState, 0, + mViewport.displayId, 1, &mPointerSimple.lastProperties, &mPointerSimple.lastCoords, mOrientedXPrecision, mOrientedYPrecision, mPointerSimple.downTime); @@ -5071,6 +5482,7 @@ void TouchInputMapper::dispatchPointerSimple(nsecs_t when, uint32_t policyFlags, // Send down. NotifyMotionArgs args(when, getDeviceId(), mSource, policyFlags, AMOTION_EVENT_ACTION_DOWN, 0, metaState, mCurrentButtonState, 0, + mViewport.displayId, 1, &mPointerSimple.currentProperties, &mPointerSimple.currentCoords, mOrientedXPrecision, mOrientedYPrecision, mPointerSimple.downTime); @@ -5080,6 +5492,7 @@ void TouchInputMapper::dispatchPointerSimple(nsecs_t when, uint32_t policyFlags, // Send move. NotifyMotionArgs args(when, getDeviceId(), mSource, policyFlags, AMOTION_EVENT_ACTION_MOVE, 0, metaState, mCurrentButtonState, 0, + mViewport.displayId, 1, &mPointerSimple.currentProperties, &mPointerSimple.currentCoords, mOrientedXPrecision, mOrientedYPrecision, mPointerSimple.downTime); @@ -5093,6 +5506,7 @@ void TouchInputMapper::dispatchPointerSimple(nsecs_t when, uint32_t policyFlags, // Send hover enter. NotifyMotionArgs args(when, getDeviceId(), mSource, policyFlags, AMOTION_EVENT_ACTION_HOVER_ENTER, 0, metaState, mCurrentButtonState, 0, + mViewport.displayId, 1, &mPointerSimple.currentProperties, &mPointerSimple.currentCoords, mOrientedXPrecision, mOrientedYPrecision, mPointerSimple.downTime); @@ -5102,6 +5516,7 @@ void TouchInputMapper::dispatchPointerSimple(nsecs_t when, uint32_t policyFlags, // Send hover move. NotifyMotionArgs args(when, getDeviceId(), mSource, policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, metaState, mCurrentButtonState, 0, + mViewport.displayId, 1, &mPointerSimple.currentProperties, &mPointerSimple.currentCoords, mOrientedXPrecision, mOrientedYPrecision, mPointerSimple.downTime); @@ -5122,6 +5537,7 @@ void TouchInputMapper::dispatchPointerSimple(nsecs_t when, uint32_t policyFlags, NotifyMotionArgs args(when, getDeviceId(), mSource, policyFlags, AMOTION_EVENT_ACTION_SCROLL, 0, metaState, mCurrentButtonState, 0, + mViewport.displayId, 1, &mPointerSimple.currentProperties, &pointerCoords, mOrientedXPrecision, mOrientedYPrecision, mPointerSimple.downTime); @@ -5183,7 +5599,8 @@ void TouchInputMapper::dispatchMotion(nsecs_t when, uint32_t policyFlags, uint32 NotifyMotionArgs args(when, getDeviceId(), source, policyFlags, action, flags, metaState, buttonState, edgeFlags, - pointerCount, pointerProperties, pointerCoords, xPrecision, yPrecision, downTime); + mViewport.displayId, pointerCount, pointerProperties, pointerCoords, + xPrecision, yPrecision, downTime); getListener()->notifyMotion(&args); } @@ -5550,6 +5967,10 @@ void SingleTouchInputMapper::configureRawPointerAxes() { getAbsoluteAxisInfo(ABS_TILT_Y, &mRawPointerAxes.tiltY); } +bool SingleTouchInputMapper::hasStylus() const { + return mTouchButtonAccumulator.hasStylus(); +} + // --- MultiTouchInputMapper --- @@ -5684,12 +6105,19 @@ void MultiTouchInputMapper::configureRawPointerAxes() { getDeviceName().string(), slotCount, MAX_SLOTS); slotCount = MAX_SLOTS; } - mMultiTouchMotionAccumulator.configure(slotCount, true /*usingSlotsProtocol*/); + mMultiTouchMotionAccumulator.configure(getDevice(), + slotCount, true /*usingSlotsProtocol*/); } else { - mMultiTouchMotionAccumulator.configure(MAX_POINTERS, false /*usingSlotsProtocol*/); + mMultiTouchMotionAccumulator.configure(getDevice(), + MAX_POINTERS, false /*usingSlotsProtocol*/); } } +bool MultiTouchInputMapper::hasStylus() const { + return mMultiTouchMotionAccumulator.hasStylus() + || mTouchButtonAccumulator.hasStylus(); +} + // --- JoystickInputMapper --- @@ -5709,15 +6137,42 @@ void JoystickInputMapper::populateDeviceInfo(InputDeviceInfo* info) { for (size_t i = 0; i < mAxes.size(); i++) { const Axis& axis = mAxes.valueAt(i); - info->addMotionRange(axis.axisInfo.axis, AINPUT_SOURCE_JOYSTICK, - axis.min, axis.max, axis.flat, axis.fuzz); + addMotionRange(axis.axisInfo.axis, axis, info); + if (axis.axisInfo.mode == AxisInfo::MODE_SPLIT) { - info->addMotionRange(axis.axisInfo.highAxis, AINPUT_SOURCE_JOYSTICK, - axis.min, axis.max, axis.flat, axis.fuzz); + addMotionRange(axis.axisInfo.highAxis, axis, info); + } } } +void JoystickInputMapper::addMotionRange(int32_t axisId, const Axis& axis, + InputDeviceInfo* info) { + info->addMotionRange(axisId, AINPUT_SOURCE_JOYSTICK, + axis.min, axis.max, axis.flat, axis.fuzz, axis.resolution); + /* In order to ease the transition for developers from using the old axes + * to the newer, more semantically correct axes, we'll continue to register + * the old axes as duplicates of their corresponding new ones. */ + int32_t compatAxis = getCompatAxis(axisId); + if (compatAxis >= 0) { + info->addMotionRange(compatAxis, AINPUT_SOURCE_JOYSTICK, + axis.min, axis.max, axis.flat, axis.fuzz, axis.resolution); + } +} + +/* A mapping from axes the joystick actually has to the axes that should be + * artificially created for compatibility purposes. + * Returns -1 if no compatibility axis is needed. */ +int32_t JoystickInputMapper::getCompatAxis(int32_t axis) { + switch(axis) { + case AMOTION_EVENT_AXIS_LTRIGGER: + return AMOTION_EVENT_AXIS_BRAKE; + case AMOTION_EVENT_AXIS_RTRIGGER: + return AMOTION_EVENT_AXIS_GAS; + } + return -1; +} + void JoystickInputMapper::dump(String8& dump) { dump.append(INDENT2 "Joystick Input Mapper:\n"); @@ -5743,8 +6198,8 @@ void JoystickInputMapper::dump(String8& dump) { dump.append(" (invert)"); } - dump.appendFormat(": min=%0.5f, max=%0.5f, flat=%0.5f, fuzz=%0.5f\n", - axis.min, axis.max, axis.flat, axis.fuzz); + dump.appendFormat(": min=%0.5f, max=%0.5f, flat=%0.5f, fuzz=%0.5f, resolution=%0.5f\n", + axis.min, axis.max, axis.flat, axis.fuzz, axis.resolution); dump.appendFormat(INDENT4 " scale=%0.5f, offset=%0.5f, " "highScale=%0.5f, highOffset=%0.5f\n", axis.scale, axis.offset, axis.highScale, axis.highOffset); @@ -5790,18 +6245,21 @@ void JoystickInputMapper::configure(nsecs_t when, float highScale = 1.0f / (rawAxisInfo.maxValue - axisInfo.splitValue); axis.initialize(rawAxisInfo, axisInfo, explicitlyMapped, scale, 0.0f, highScale, 0.0f, - 0.0f, 1.0f, rawFlat * scale, rawAxisInfo.fuzz * scale); + 0.0f, 1.0f, rawFlat * scale, rawAxisInfo.fuzz * scale, + rawAxisInfo.resolution * scale); } else if (isCenteredAxis(axisInfo.axis)) { float scale = 2.0f / (rawAxisInfo.maxValue - rawAxisInfo.minValue); float offset = avg(rawAxisInfo.minValue, rawAxisInfo.maxValue) * -scale; axis.initialize(rawAxisInfo, axisInfo, explicitlyMapped, scale, offset, scale, offset, - -1.0f, 1.0f, rawFlat * scale, rawAxisInfo.fuzz * scale); + -1.0f, 1.0f, rawFlat * scale, rawAxisInfo.fuzz * scale, + rawAxisInfo.resolution * scale); } else { float scale = 1.0f / (rawAxisInfo.maxValue - rawAxisInfo.minValue); axis.initialize(rawAxisInfo, axisInfo, explicitlyMapped, scale, 0.0f, scale, 0.0f, - 0.0f, 1.0f, rawFlat * scale, rawAxisInfo.fuzz * scale); + 0.0f, 1.0f, rawFlat * scale, rawAxisInfo.fuzz * scale, + rawAxisInfo.resolution * scale); } // To eliminate noise while the joystick is at rest, filter out small variations @@ -5905,7 +6363,7 @@ void JoystickInputMapper::reset(nsecs_t when) { void JoystickInputMapper::process(const RawEvent* rawEvent) { switch (rawEvent->type) { case EV_ABS: { - ssize_t index = mAxes.indexOfKey(rawEvent->scanCode); + ssize_t index = mAxes.indexOfKey(rawEvent->code); if (index >= 0) { Axis& axis = mAxes.editValueAt(index); float newValue, highNewValue; @@ -5941,7 +6399,7 @@ void JoystickInputMapper::process(const RawEvent* rawEvent) { } case EV_SYN: - switch (rawEvent->scanCode) { + switch (rawEvent->code) { case SYN_REPORT: sync(rawEvent->when, false /*force*/); break; @@ -5969,13 +6427,14 @@ void JoystickInputMapper::sync(nsecs_t when, bool force) { size_t numAxes = mAxes.size(); for (size_t i = 0; i < numAxes; i++) { const Axis& axis = mAxes.valueAt(i); - pointerCoords.setAxisValue(axis.axisInfo.axis, axis.currentValue); + setPointerCoordsAxisValue(&pointerCoords, axis.axisInfo.axis, axis.currentValue); if (axis.axisInfo.mode == AxisInfo::MODE_SPLIT) { - pointerCoords.setAxisValue(axis.axisInfo.highAxis, axis.highCurrentValue); + setPointerCoordsAxisValue(&pointerCoords, axis.axisInfo.highAxis, + axis.highCurrentValue); } } - // Moving a joystick axis should not wake the devide because joysticks can + // Moving a joystick axis should not wake the device because joysticks can // be fairly noisy even when not in use. On the other hand, pushing a gamepad // button will likely wake the device. // TODO: Use the input device configuration to control this behavior more finely. @@ -5983,10 +6442,23 @@ void JoystickInputMapper::sync(nsecs_t when, bool force) { NotifyMotionArgs args(when, getDeviceId(), AINPUT_SOURCE_JOYSTICK, policyFlags, AMOTION_EVENT_ACTION_MOVE, 0, metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE, - 1, &pointerProperties, &pointerCoords, 0, 0, 0); + ADISPLAY_ID_NONE, 1, &pointerProperties, &pointerCoords, 0, 0, 0); getListener()->notifyMotion(&args); } +void JoystickInputMapper::setPointerCoordsAxisValue(PointerCoords* pointerCoords, + int32_t axis, float value) { + pointerCoords->setAxisValue(axis, value); + /* In order to ease the transition for developers from using the old axes + * to the newer, more semantically correct axes, we'll continue to produce + * values for the old axes as mirrors of the value of their corresponding + * new axes. */ + int32_t compatAxis = getCompatAxis(axis); + if (compatAxis >= 0) { + pointerCoords->setAxisValue(compatAxis, value); + } +} + bool JoystickInputMapper::filterAxes(bool force) { bool atLeastOneSignificantChange = force; size_t numAxes = mAxes.size(); diff --git a/widget/gonk/libui/InputReader.h b/widget/gonk/libui/InputReader.h index 7bd9958c6bc2..5c790fdb866e 100644 --- a/widget/gonk/libui/InputReader.h +++ b/widget/gonk/libui/InputReader.h @@ -22,22 +22,94 @@ #include "InputListener.h" #include "Input.h" -#include "DisplayInfo.h" +#include "VelocityControl.h" +#include "VelocityTracker.h" #include #include -#include "Timers.h" +#include #include -#include "String8.h" -#include "BitSet.h" +#include +#include #include #include +// Maximum supported size of a vibration pattern. +// Must be at least 2. +#define MAX_VIBRATE_PATTERN_SIZE 100 + +// Maximum allowable delay value in a vibration pattern before +// which the delay will be truncated. +#define MAX_VIBRATE_PATTERN_DELAY_NSECS (1000000 * 1000000000LL) + namespace android { class InputDevice; class InputMapper; +/* + * Describes how coordinates are mapped on a physical display. + * See com.android.server.display.DisplayViewport. + */ +struct DisplayViewport { + int32_t displayId; // -1 if invalid + int32_t orientation; + int32_t logicalLeft; + int32_t logicalTop; + int32_t logicalRight; + int32_t logicalBottom; + int32_t physicalLeft; + int32_t physicalTop; + int32_t physicalRight; + int32_t physicalBottom; + int32_t deviceWidth; + int32_t deviceHeight; + + DisplayViewport() : + displayId(ADISPLAY_ID_NONE), orientation(DISPLAY_ORIENTATION_0), + logicalLeft(0), logicalTop(0), logicalRight(0), logicalBottom(0), + physicalLeft(0), physicalTop(0), physicalRight(0), physicalBottom(0), + deviceWidth(0), deviceHeight(0) { + } + + bool operator==(const DisplayViewport& other) const { + return displayId == other.displayId + && orientation == other.orientation + && logicalLeft == other.logicalLeft + && logicalTop == other.logicalTop + && logicalRight == other.logicalRight + && logicalBottom == other.logicalBottom + && physicalLeft == other.physicalLeft + && physicalTop == other.physicalTop + && physicalRight == other.physicalRight + && physicalBottom == other.physicalBottom + && deviceWidth == other.deviceWidth + && deviceHeight == other.deviceHeight; + } + + bool operator!=(const DisplayViewport& other) const { + return !(*this == other); + } + + inline bool isValid() const { + return displayId >= 0; + } + + void setNonDisplayViewport(int32_t width, int32_t height) { + displayId = ADISPLAY_ID_NONE; + orientation = DISPLAY_ORIENTATION_0; + logicalLeft = 0; + logicalTop = 0; + logicalRight = width; + logicalBottom = height; + physicalLeft = 0; + physicalTop = 0; + physicalRight = width; + physicalBottom = height; + deviceWidth = width; + deviceHeight = height; + } +}; /* * Input reader configuration. @@ -59,6 +131,12 @@ struct InputReaderConfiguration { // The visible touches option changed. CHANGE_SHOW_TOUCHES = 1 << 3, + // The keyboard layouts must be reloaded. + CHANGE_KEYBOARD_LAYOUTS = 1 << 4, + + // The device name alias supplied by the may have changed for some devices. + CHANGE_DEVICE_ALIAS = 1 << 5, + // All devices must be reopened. CHANGE_MUST_REOPEN = 1 << 31, }; @@ -164,25 +242,12 @@ struct InputReaderConfiguration { pointerGestureZoomSpeedRatio(0.3f), showTouches(false) { } - bool getDisplayInfo(int32_t displayId, bool external, - int32_t* width, int32_t* height, int32_t* orientation) const; - - void setDisplayInfo(int32_t displayId, bool external, - int32_t width, int32_t height, int32_t orientation); + bool getDisplayInfo(bool external, DisplayViewport* outViewport) const; + void setDisplayInfo(bool external, const DisplayViewport& viewport); private: - struct DisplayInfo { - int32_t width; - int32_t height; - int32_t orientation; - - DisplayInfo() : - width(-1), height(-1), orientation(DISPLAY_ORIENTATION_0) { - } - }; - - DisplayInfo mInternalDisplay; - DisplayInfo mExternalDisplay; + DisplayViewport mInternalDisplay; + DisplayViewport mExternalDisplay; }; @@ -209,6 +274,17 @@ public: /* Gets a pointer controller associated with the specified cursor device (ie. a mouse). */ virtual sp obtainPointerController(int32_t deviceId) = 0; + + /* Notifies the input reader policy that some input devices have changed + * and provides information about all current input devices. + */ + virtual void notifyInputDevicesChanged(const Vector& inputDevices) = 0; + + /* Gets the keyboard layout for a particular input device. */ + virtual sp getKeyboardLayoutOverlay(const String8& inputDeviceDescriptor) = 0; + + /* Gets a user-supplied alias for a particular input device, or an empty string if none. */ + virtual String8 getDeviceAlias(const InputDeviceIdentifier& identifier) = 0; }; @@ -234,22 +310,11 @@ public: */ virtual void loopOnce() = 0; - /* Gets the current input device configuration. + /* Gets information about all input devices. * * This method may be called on any thread (usually by the input manager). */ - virtual void getInputConfiguration(InputConfiguration* outConfiguration) = 0; - - /* Gets information about the specified input device. - * Returns OK if the device information was obtained or NAME_NOT_FOUND if there - * was no such device. - * - * This method may be called on any thread (usually by the input manager). - */ - virtual status_t getInputDeviceInfo(int32_t deviceId, InputDeviceInfo* outDeviceInfo) = 0; - - /* Gets the list of all registered device ids. */ - virtual void getInputDeviceIds(Vector& outDeviceIds) = 0; + virtual void getInputDevices(Vector& outInputDevices) = 0; /* Query current input state. */ virtual int32_t getScanCodeState(int32_t deviceId, uint32_t sourceMask, @@ -267,6 +332,11 @@ public: * The changes flag is a bitfield that indicates what has changed and whether * the input devices must all be reopened. */ virtual void requestRefreshConfiguration(uint32_t changes) = 0; + + /* Controls the vibrator of a particular input device. */ + virtual void vibrate(int32_t deviceId, const nsecs_t* pattern, size_t patternSize, + ssize_t repeat, int32_t token) = 0; + virtual void cancelVibrate(int32_t deviceId, int32_t token) = 0; }; @@ -288,6 +358,7 @@ public: virtual void fadePointer() = 0; virtual void requestTimeoutAtTime(nsecs_t when) = 0; + virtual int32_t bumpGeneration() = 0; virtual InputReaderPolicyInterface* getPolicy() = 0; virtual InputListenerInterface* getListener() = 0; @@ -318,10 +389,7 @@ public: virtual void loopOnce(); - virtual void getInputConfiguration(InputConfiguration* outConfiguration); - - virtual status_t getInputDeviceInfo(int32_t deviceId, InputDeviceInfo* outDeviceInfo); - virtual void getInputDeviceIds(Vector& outDeviceIds); + virtual void getInputDevices(Vector& outInputDevices); virtual int32_t getScanCodeState(int32_t deviceId, uint32_t sourceMask, int32_t scanCode); @@ -335,10 +403,14 @@ public: virtual void requestRefreshConfiguration(uint32_t changes); + virtual void vibrate(int32_t deviceId, const nsecs_t* pattern, size_t patternSize, + ssize_t repeat, int32_t token); + virtual void cancelVibrate(int32_t deviceId, int32_t token); + protected: // These members are protected so they can be instrumented by test cases. virtual InputDevice* createDeviceLocked(int32_t deviceId, - const String8& name, uint32_t classes); + const InputDeviceIdentifier& identifier, uint32_t classes); class ContextImpl : public InputReaderContext { InputReader* mReader; @@ -353,6 +425,7 @@ protected: InputDevice* device, int32_t keyCode, int32_t scanCode); virtual void fadePointer(); virtual void requestTimeoutAtTime(nsecs_t when); + virtual int32_t bumpGeneration(); virtual InputReaderPolicyInterface* getPolicy(); virtual InputListenerInterface* getListener(); virtual EventHubInterface* getEventHub(); @@ -363,6 +436,8 @@ protected: private: Mutex mLock; + Condition mReaderIsAliveCondition; + sp mEventHub; sp mPolicy; sp mQueuedListener; @@ -391,8 +466,10 @@ private: void fadePointerLocked(); - InputConfiguration mInputConfiguration; - void updateInputConfigurationLocked(); + int32_t mGeneration; + int32_t bumpGenerationLocked(); + + void getInputDevicesLocked(Vector& outInputDevices); nsecs_t mDisableVirtualKeysTimeout; void disableVirtualKeysUntilLocked(nsecs_t time); @@ -431,12 +508,14 @@ private: /* Represents the state of a single input device. */ class InputDevice { public: - InputDevice(InputReaderContext* context, int32_t id, const String8& name, uint32_t classes); + InputDevice(InputReaderContext* context, int32_t id, int32_t generation, + const InputDeviceIdentifier& identifier, uint32_t classes); ~InputDevice(); inline InputReaderContext* getContext() { return mContext; } inline int32_t getId() { return mId; } - inline const String8& getName() { return mName; } + inline int32_t getGeneration() { return mGeneration; } + inline const String8& getName() { return mIdentifier.name; } inline uint32_t getClasses() { return mClasses; } inline uint32_t getSources() { return mSources; } @@ -458,11 +537,15 @@ public: int32_t getSwitchState(uint32_t sourceMask, int32_t switchCode); bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags); + void vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat, int32_t token); + void cancelVibrate(int32_t token); int32_t getMetaState(); void fadePointer(); + void bumpGeneration(); + void notifyReset(nsecs_t when); inline const PropertyMap& getConfiguration() { return mConfiguration; } @@ -472,6 +555,12 @@ public: return getEventHub()->hasScanCode(mId, code); } + bool hasAbsoluteAxis(int32_t code) { + RawAbsoluteAxisInfo info; + getEventHub()->getAbsoluteAxisInfo(mId, code, &info); + return info.valid; + } + bool isKeyPressed(int32_t code) { return getEventHub()->getScanCodeState(mId, code) == AKEY_STATE_DOWN; } @@ -485,7 +574,9 @@ public: private: InputReaderContext* mContext; int32_t mId; - String8 mName; + int32_t mGeneration; + InputDeviceIdentifier mIdentifier; + String8 mAlias; uint32_t mClasses; Vector mMappers; @@ -591,9 +682,11 @@ public: int32_t getToolType() const; bool isToolActive() const; bool isHovering() const; + bool hasStylus() const; private: bool mHaveBtnTouch; + bool mHaveStylus; bool mBtnTouch; bool mBtnStylus; @@ -699,6 +792,10 @@ struct CookedPointerData { void clear(); void copyFrom(const CookedPointerData& other); + inline const PointerCoords& pointerCoordsForId(uint32_t id) const { + return pointerCoords[idToIndex[id]]; + } + inline bool isHovering(uint32_t pointerIndex) { return hoveringIdBits.hasBit(pointerProperties[pointerIndex].id); } @@ -781,10 +878,11 @@ public: MultiTouchMotionAccumulator(); ~MultiTouchMotionAccumulator(); - void configure(size_t slotCount, bool usingSlotsProtocol); + void configure(InputDevice* device, size_t slotCount, bool usingSlotsProtocol); void reset(InputDevice* device); void process(const RawEvent* rawEvent); void finishSync(); + bool hasStylus() const; inline size_t getSlotCount() const { return mSlotCount; } inline const Slot* getSlot(size_t index) const { return &mSlots[index]; } @@ -794,6 +892,7 @@ private: Slot* mSlots; size_t mSlotCount; bool mUsingSlotsProtocol; + bool mHaveStylus; void clearSlots(int32_t initialSlot); }; @@ -837,6 +936,9 @@ public: virtual int32_t getSwitchState(uint32_t sourceMask, int32_t switchCode); virtual bool markSupportedKeyCodes(uint32_t sourceMask, size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags); + virtual void vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat, + int32_t token); + virtual void cancelVibrate(int32_t token); virtual int32_t getMetaState(); @@ -847,6 +949,7 @@ protected: InputReaderContext* mContext; status_t getAbsoluteAxisInfo(int32_t axis, RawAbsoluteAxisInfo* axisInfo); + void bumpGeneration(); static void dumpRawAbsoluteAxisInfo(String8& dump, const RawAbsoluteAxisInfo& axis, const char* name); @@ -864,7 +967,40 @@ public: virtual int32_t getSwitchState(uint32_t sourceMask, int32_t switchCode); private: - void processSwitch(nsecs_t when, int32_t switchCode, int32_t switchValue); + uint32_t mUpdatedSwitchValues; + uint32_t mUpdatedSwitchMask; + + void processSwitch(int32_t switchCode, int32_t switchValue); + void sync(nsecs_t when); +}; + + +class VibratorInputMapper : public InputMapper { +public: + VibratorInputMapper(InputDevice* device); + virtual ~VibratorInputMapper(); + + virtual uint32_t getSources(); + virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo); + virtual void process(const RawEvent* rawEvent); + + virtual void vibrate(const nsecs_t* pattern, size_t patternSize, ssize_t repeat, + int32_t token); + virtual void cancelVibrate(int32_t token); + virtual void timeoutExpired(nsecs_t when); + virtual void dump(String8& dump); + +private: + bool mVibrating; + nsecs_t mPattern[MAX_VIBRATE_PATTERN_SIZE]; + size_t mPatternSize; + ssize_t mRepeat; + int32_t mToken; + ssize_t mIndex; + nsecs_t mNextStepTime; + + void nextStep(); + void stopVibrating(); }; @@ -902,6 +1038,8 @@ private: int32_t mMetaState; nsecs_t mDownTime; // time of most recent key down + int32_t mCurrentHidUsage; // most recent HID usage seen this packet, or 0 if none + struct LedState { bool avail; // led is available bool on; // we think the led is currently on @@ -912,7 +1050,7 @@ private: // Immutable configuration parameters. struct Parameters { - int32_t associatedDisplayId; + bool hasAssociatedDisplay; bool orientationAware; } mParameters; @@ -962,7 +1100,7 @@ private: }; Mode mode; - int32_t associatedDisplayId; + bool hasAssociatedDisplay; bool orientationAware; } mParameters; @@ -1047,6 +1185,7 @@ protected: DEVICE_MODE_DISABLED, // input is disabled DEVICE_MODE_DIRECT, // direct mapping (touchscreen) DEVICE_MODE_UNSCALED, // unscaled mapping (touchpad) + DEVICE_MODE_NAVIGATION, // unscaled mapping with assist gesture (touch navigation) DEVICE_MODE_POINTER, // pointer mapping (pointer) }; DeviceMode mDeviceMode; @@ -1059,11 +1198,12 @@ protected: enum DeviceType { DEVICE_TYPE_TOUCH_SCREEN, DEVICE_TYPE_TOUCH_PAD, + DEVICE_TYPE_TOUCH_NAVIGATION, DEVICE_TYPE_POINTER, }; DeviceType deviceType; - int32_t associatedDisplayId; + bool hasAssociatedDisplay; bool associatedDisplayIsExternal; bool orientationAware; @@ -1082,6 +1222,7 @@ protected: SIZE_CALIBRATION_NONE, SIZE_CALIBRATION_GEOMETRIC, SIZE_CALIBRATION_DIAMETER, + SIZE_CALIBRATION_BOX, SIZE_CALIBRATION_AREA, }; @@ -1127,6 +1268,14 @@ protected: bool haveDistanceScale; float distanceScale; + enum CoverageCalibration { + COVERAGE_CALIBRATION_DEFAULT, + COVERAGE_CALIBRATION_NONE, + COVERAGE_CALIBRATION_BOX, + }; + + CoverageCalibration coverageCalibration; + inline void applySizeScaleAndBias(float* outSize) const { if (haveSizeScale) { *outSize *= sizeScale; @@ -1186,24 +1335,35 @@ protected: virtual void parseCalibration(); virtual void resolveCalibration(); virtual void dumpCalibration(String8& dump); + virtual bool hasStylus() const = 0; virtual void syncTouch(nsecs_t when, bool* outHavePointerIds) = 0; private: - // The surface orientation and width and height set by configureSurface(). - int32_t mSurfaceOrientation; + // The current viewport. + // The components of the viewport are specified in the display's rotated orientation. + DisplayViewport mViewport; + + // The surface orientation, width and height set by configureSurface(). + // The width and height are derived from the viewport but are specified + // in the natural orientation. + // The surface origin specifies how the surface coordinates should be translated + // to align with the logical display coordinate space. + // The orientation may be different from the viewport orientation as it specifies + // the rotation of the surface coordinates required to produce the viewport's + // requested orientation, so it will depend on whether the device is orientation aware. int32_t mSurfaceWidth; int32_t mSurfaceHeight; - - // The associated display orientation and width and height set by configureSurface(). - int32_t mAssociatedDisplayOrientation; - int32_t mAssociatedDisplayWidth; - int32_t mAssociatedDisplayHeight; + int32_t mSurfaceLeft; + int32_t mSurfaceTop; + int32_t mSurfaceOrientation; // Translation and scaling factors, orientation-independent. + float mXTranslate; float mXScale; float mXPrecision; + float mYTranslate; float mYScale; float mYPrecision; @@ -1213,7 +1373,6 @@ private: float mSizeScale; - float mOrientationCenter; float mOrientationScale; float mDistanceScale; @@ -1265,8 +1424,6 @@ private: } mOrientedRanges; // Oriented dimensions and precision. - float mOrientedSurfaceWidth; - float mOrientedSurfaceHeight; float mOrientedXPrecision; float mOrientedYPrecision; @@ -1534,6 +1691,7 @@ public: protected: virtual void syncTouch(nsecs_t when, bool* outHavePointerIds); virtual void configureRawPointerAxes(); + virtual bool hasStylus() const; private: SingleTouchMotionAccumulator mSingleTouchMotionAccumulator; @@ -1551,6 +1709,7 @@ public: protected: virtual void syncTouch(nsecs_t when, bool* outHavePointerIds); virtual void configureRawPointerAxes(); + virtual bool hasStylus() const; private: MultiTouchMotionAccumulator mMultiTouchMotionAccumulator; @@ -1585,10 +1744,11 @@ private: float highScale; // scale factor from raw to normalized values of high split float highOffset; // offset to add after scaling for normalization of high split - float min; // normalized inclusive minimum - float max; // normalized inclusive maximum - float flat; // normalized flat region size - float fuzz; // normalized error tolerance + float min; // normalized inclusive minimum + float max; // normalized inclusive maximum + float flat; // normalized flat region size + float fuzz; // normalized error tolerance + float resolution; // normalized resolution in units/mm float filter; // filter out small variations of this size float currentValue; // current value @@ -1599,7 +1759,7 @@ private: void initialize(const RawAbsoluteAxisInfo& rawAxisInfo, const AxisInfo& axisInfo, bool explicitlyMapped, float scale, float offset, float highScale, float highOffset, - float min, float max, float flat, float fuzz) { + float min, float max, float flat, float fuzz, float resolution) { this->rawAxisInfo = rawAxisInfo; this->axisInfo = axisInfo; this->explicitlyMapped = explicitlyMapped; @@ -1611,6 +1771,7 @@ private: this->max = max; this->flat = flat; this->fuzz = fuzz; + this->resolution = resolution; this->filter = 0; resetValue(); } @@ -1638,6 +1799,11 @@ private: float newValue, float currentValue, float thresholdValue); static bool isCenteredAxis(int32_t axis); + static int32_t getCompatAxis(int32_t axis); + + static void addMotionRange(int32_t axisId, const Axis& axis, InputDeviceInfo* info); + static void setPointerCoordsAxisValue(PointerCoords* pointerCoords, int32_t axis, + float value); }; } // namespace android diff --git a/widget/gonk/libui/InputTransport.cpp b/widget/gonk/libui/InputTransport.cpp index 95df2e49e9d7..3f0fcb047118 100644 --- a/widget/gonk/libui/InputTransport.cpp +++ b/widget/gonk/libui/InputTransport.cpp @@ -7,325 +7,235 @@ //#define LOG_NDEBUG 0 -// Log debug messages about channel signalling (send signal, receive signal) -#define DEBUG_CHANNEL_SIGNALS 0 +// Log debug messages about channel messages (send message, receive message) +#define DEBUG_CHANNEL_MESSAGES 0 // Log debug messages whenever InputChannel objects are created/destroyed #define DEBUG_CHANNEL_LIFECYCLE 0 -// Log debug messages about transport actions (initialize, reset, publish, ...) +// Log debug messages about transport actions #define DEBUG_TRANSPORT_ACTIONS 0 +// Log debug messages about touch event resampling +#define DEBUG_RESAMPLING 0 + -#include #include "cutils_log.h" +#include #include #include -#include #include "InputTransport.h" #include +#include +#include +#include + namespace android { -#define ROUND_UP(value, boundary) (((value) + (boundary) - 1) & ~((boundary) - 1)) -#define MIN_HISTORY_DEPTH 20 +// Socket buffer size. The default is typically about 128KB, which is much larger than +// we really need. So we make it smaller. It just needs to be big enough to hold +// a few dozen large multi-finger motion events in the case where an application gets +// behind processing touches. +static const size_t SOCKET_BUFFER_SIZE = 32 * 1024; -// Must be at least sizeof(InputMessage) + sufficient space for pointer data -static const int DEFAULT_MESSAGE_BUFFER_SIZE = ROUND_UP( - sizeof(InputMessage) + MIN_HISTORY_DEPTH - * (sizeof(InputMessage::SampleData) + MAX_POINTERS * sizeof(PointerCoords)), - 4096); +// Nanoseconds per milliseconds. +static const nsecs_t NANOS_PER_MS = 1000000; -// Signal sent by the producer to the consumer to inform it that a new message is -// available to be consumed in the shared memory buffer. -static const char INPUT_SIGNAL_DISPATCH = 'D'; +// Latency added during resampling. A few milliseconds doesn't hurt much but +// reduces the impact of mispredicted touch positions. +static const nsecs_t RESAMPLE_LATENCY = 5 * NANOS_PER_MS; -// Signal sent by the consumer to the producer to inform it that it has finished -// consuming the most recent message and it handled it. -static const char INPUT_SIGNAL_FINISHED_HANDLED = 'f'; +// Minimum time difference between consecutive samples before attempting to resample. +static const nsecs_t RESAMPLE_MIN_DELTA = 2 * NANOS_PER_MS; -// Signal sent by the consumer to the producer to inform it that it has finished -// consuming the most recent message but it did not handle it. -static const char INPUT_SIGNAL_FINISHED_UNHANDLED = 'u'; +// Maximum time to predict forward from the last known state, to avoid predicting too +// far into the future. This time is further bounded by 50% of the last time delta. +static const nsecs_t RESAMPLE_MAX_PREDICTION = 8 * NANOS_PER_MS; + +template +inline static T min(const T& a, const T& b) { + return a < b ? a : b; +} + +inline static float lerp(float a, float b, float alpha) { + return a + alpha * (b - a); +} + +// --- InputMessage --- + +bool InputMessage::isValid(size_t actualSize) const { + if (size() == actualSize) { + switch (header.type) { + case TYPE_KEY: + return true; + case TYPE_MOTION: + return body.motion.pointerCount > 0 + && body.motion.pointerCount <= MAX_POINTERS; + case TYPE_FINISHED: + return true; + } + } + return false; +} + +size_t InputMessage::size() const { + switch (header.type) { + case TYPE_KEY: + return sizeof(Header) + body.key.size(); + case TYPE_MOTION: + return sizeof(Header) + body.motion.size(); + case TYPE_FINISHED: + return sizeof(Header) + body.finished.size(); + } + return sizeof(Header); +} // --- InputChannel --- -InputChannel::InputChannel(const String8& name, int32_t ashmemFd, int32_t receivePipeFd, - int32_t sendPipeFd) : - mName(name), mAshmemFd(ashmemFd), mReceivePipeFd(receivePipeFd), mSendPipeFd(sendPipeFd) { +InputChannel::InputChannel(const String8& name, int fd) : + mName(name), mFd(fd) { #if DEBUG_CHANNEL_LIFECYCLE - ALOGD("Input channel constructed: name='%s', ashmemFd=%d, receivePipeFd=%d, sendPipeFd=%d", - mName.string(), ashmemFd, receivePipeFd, sendPipeFd); + ALOGD("Input channel constructed: name='%s', fd=%d", + mName.string(), fd); #endif - int result = fcntl(mReceivePipeFd, F_SETFL, O_NONBLOCK); - LOG_ALWAYS_FATAL_IF(result != 0, "channel '%s' ~ Could not make receive pipe " - "non-blocking. errno=%d", mName.string(), errno); - - result = fcntl(mSendPipeFd, F_SETFL, O_NONBLOCK); - LOG_ALWAYS_FATAL_IF(result != 0, "channel '%s' ~ Could not make send pipe " + int result = fcntl(mFd, F_SETFL, O_NONBLOCK); + LOG_ALWAYS_FATAL_IF(result != 0, "channel '%s' ~ Could not make socket " "non-blocking. errno=%d", mName.string(), errno); } InputChannel::~InputChannel() { #if DEBUG_CHANNEL_LIFECYCLE - ALOGD("Input channel destroyed: name='%s', ashmemFd=%d, receivePipeFd=%d, sendPipeFd=%d", - mName.string(), mAshmemFd, mReceivePipeFd, mSendPipeFd); + ALOGD("Input channel destroyed: name='%s', fd=%d", + mName.string(), mFd); #endif - ::close(mAshmemFd); - ::close(mReceivePipeFd); - ::close(mSendPipeFd); + ::close(mFd); } status_t InputChannel::openInputChannelPair(const String8& name, sp& outServerChannel, sp& outClientChannel) { - status_t result; - - String8 ashmemName("InputChannel "); - ashmemName.append(name); - int serverAshmemFd = ashmem_create_region(ashmemName.string(), DEFAULT_MESSAGE_BUFFER_SIZE); - if (serverAshmemFd < 0) { - result = -errno; - ALOGE("channel '%s' ~ Could not create shared memory region. errno=%d", + int sockets[2]; + if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets)) { + status_t result = -errno; + ALOGE("channel '%s' ~ Could not create socket pair. errno=%d", name.string(), errno); - } else { - result = ashmem_set_prot_region(serverAshmemFd, PROT_READ | PROT_WRITE); - if (result < 0) { - ALOGE("channel '%s' ~ Error %d trying to set protection of ashmem fd %d.", - name.string(), result, serverAshmemFd); - } else { - // Dup the file descriptor because the server and client input channel objects that - // are returned may have different lifetimes but they share the same shared memory region. - int clientAshmemFd; - clientAshmemFd = dup(serverAshmemFd); - if (clientAshmemFd < 0) { - result = -errno; - ALOGE("channel '%s' ~ Could not dup() shared memory region fd. errno=%d", - name.string(), errno); - } else { - int forward[2]; - if (pipe(forward)) { - result = -errno; - ALOGE("channel '%s' ~ Could not create forward pipe. errno=%d", - name.string(), errno); - } else { - int reverse[2]; - if (pipe(reverse)) { - result = -errno; - ALOGE("channel '%s' ~ Could not create reverse pipe. errno=%d", - name.string(), errno); - } else { - String8 serverChannelName = name; - serverChannelName.append(" (server)"); - outServerChannel = new InputChannel(serverChannelName, - serverAshmemFd, reverse[0], forward[1]); - - String8 clientChannelName = name; - clientChannelName.append(" (client)"); - outClientChannel = new InputChannel(clientChannelName, - clientAshmemFd, forward[0], reverse[1]); - return OK; - } - ::close(forward[0]); - ::close(forward[1]); - } - ::close(clientAshmemFd); - } - } - ::close(serverAshmemFd); + outServerChannel.clear(); + outClientChannel.clear(); + return result; } - outServerChannel.clear(); - outClientChannel.clear(); - return result; + int bufferSize = SOCKET_BUFFER_SIZE; + setsockopt(sockets[0], SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize)); + setsockopt(sockets[0], SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize)); + setsockopt(sockets[1], SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize)); + setsockopt(sockets[1], SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize)); + + String8 serverChannelName = name; + serverChannelName.append(" (server)"); + outServerChannel = new InputChannel(serverChannelName, sockets[0]); + + String8 clientChannelName = name; + clientChannelName.append(" (client)"); + outClientChannel = new InputChannel(clientChannelName, sockets[1]); + return OK; } -status_t InputChannel::sendSignal(char signal) { +status_t InputChannel::sendMessage(const InputMessage* msg) { + size_t msgLength = msg->size(); ssize_t nWrite; do { - nWrite = ::write(mSendPipeFd, & signal, 1); + nWrite = ::send(mFd, msg, msgLength, MSG_DONTWAIT | MSG_NOSIGNAL); } while (nWrite == -1 && errno == EINTR); - if (nWrite == 1) { -#if DEBUG_CHANNEL_SIGNALS - ALOGD("channel '%s' ~ sent signal '%c'", mName.string(), signal); + if (nWrite < 0) { + int error = errno; +#if DEBUG_CHANNEL_MESSAGES + ALOGD("channel '%s' ~ error sending message of type %d, errno=%d", mName.string(), + msg->header.type, error); #endif - return OK; + if (error == EAGAIN || error == EWOULDBLOCK) { + return WOULD_BLOCK; + } + if (error == EPIPE || error == ENOTCONN) { + return DEAD_OBJECT; + } + return -error; } -#if DEBUG_CHANNEL_SIGNALS - ALOGD("channel '%s' ~ error sending signal '%c', errno=%d", mName.string(), signal, errno); -#endif - return -errno; -} - -status_t InputChannel::receiveSignal(char* outSignal) { - ssize_t nRead; - do { - nRead = ::read(mReceivePipeFd, outSignal, 1); - } while (nRead == -1 && errno == EINTR); - - if (nRead == 1) { -#if DEBUG_CHANNEL_SIGNALS - ALOGD("channel '%s' ~ received signal '%c'", mName.string(), *outSignal); -#endif - return OK; - } - - if (nRead == 0) { // check for EOF -#if DEBUG_CHANNEL_SIGNALS - ALOGD("channel '%s' ~ receive signal failed because peer was closed", mName.string()); + if (size_t(nWrite) != msgLength) { +#if DEBUG_CHANNEL_MESSAGES + ALOGD("channel '%s' ~ error sending message type %d, send was incomplete", + mName.string(), msg->header.type); #endif return DEAD_OBJECT; } - if (errno == EAGAIN) { -#if DEBUG_CHANNEL_SIGNALS - ALOGD("channel '%s' ~ receive signal failed because no signal available", mName.string()); +#if DEBUG_CHANNEL_MESSAGES + ALOGD("channel '%s' ~ sent message of type %d", mName.string(), msg->header.type); #endif - return WOULD_BLOCK; + return OK; +} + +status_t InputChannel::receiveMessage(InputMessage* msg) { + ssize_t nRead; + do { + nRead = ::recv(mFd, msg, sizeof(InputMessage), MSG_DONTWAIT); + } while (nRead == -1 && errno == EINTR); + + if (nRead < 0) { + int error = errno; +#if DEBUG_CHANNEL_MESSAGES + ALOGD("channel '%s' ~ receive message failed, errno=%d", mName.string(), errno); +#endif + if (error == EAGAIN || error == EWOULDBLOCK) { + return WOULD_BLOCK; + } + if (error == EPIPE || error == ENOTCONN) { + return DEAD_OBJECT; + } + return -error; } -#if DEBUG_CHANNEL_SIGNALS - ALOGD("channel '%s' ~ receive signal failed, errno=%d", mName.string(), errno); + if (nRead == 0) { // check for EOF +#if DEBUG_CHANNEL_MESSAGES + ALOGD("channel '%s' ~ receive message failed because peer was closed", mName.string()); #endif - return -errno; + return DEAD_OBJECT; + } + + if (!msg->isValid(nRead)) { +#if DEBUG_CHANNEL_MESSAGES + ALOGD("channel '%s' ~ received invalid message", mName.string()); +#endif + return BAD_VALUE; + } + +#if DEBUG_CHANNEL_MESSAGES + ALOGD("channel '%s' ~ received message of type %d", mName.string(), msg->header.type); +#endif + return OK; +} + +sp InputChannel::dup() const { + int fd = ::dup(getFd()); + return fd >= 0 ? new InputChannel(getName(), fd) : NULL; } // --- InputPublisher --- InputPublisher::InputPublisher(const sp& channel) : - mChannel(channel), mSharedMessage(NULL), - mPinned(false), mSemaphoreInitialized(false), mWasDispatched(false), - mMotionEventSampleDataTail(NULL) { + mChannel(channel) { } InputPublisher::~InputPublisher() { - reset(); - - if (mSharedMessage) { - munmap(mSharedMessage, mAshmemSize); - } -} - -status_t InputPublisher::initialize() { -#if DEBUG_TRANSPORT_ACTIONS - ALOGD("channel '%s' publisher ~ initialize", - mChannel->getName().string()); -#endif - - int ashmemFd = mChannel->getAshmemFd(); - int result = ashmem_get_size_region(ashmemFd); - if (result < 0) { - ALOGE("channel '%s' publisher ~ Error %d getting size of ashmem fd %d.", - mChannel->getName().string(), result, ashmemFd); - return UNKNOWN_ERROR; - } - mAshmemSize = (size_t) result; - - mSharedMessage = static_cast(mmap(NULL, mAshmemSize, - PROT_READ | PROT_WRITE, MAP_SHARED, ashmemFd, 0)); - if (! mSharedMessage) { - ALOGE("channel '%s' publisher ~ mmap failed on ashmem fd %d.", - mChannel->getName().string(), ashmemFd); - return NO_MEMORY; - } - - mPinned = true; - mSharedMessage->consumed = false; - - return reset(); -} - -status_t InputPublisher::reset() { -#if DEBUG_TRANSPORT_ACTIONS - ALOGD("channel '%s' publisher ~ reset", - mChannel->getName().string()); -#endif - - if (mPinned) { - // Destroy the semaphore since we are about to unpin the memory region that contains it. - int result; - if (mSemaphoreInitialized) { - if (mSharedMessage->consumed) { - result = sem_post(& mSharedMessage->semaphore); - if (result < 0) { - ALOGE("channel '%s' publisher ~ Error %d in sem_post.", - mChannel->getName().string(), errno); - return UNKNOWN_ERROR; - } - } - - result = sem_destroy(& mSharedMessage->semaphore); - if (result < 0) { - ALOGE("channel '%s' publisher ~ Error %d in sem_destroy.", - mChannel->getName().string(), errno); - return UNKNOWN_ERROR; - } - - mSemaphoreInitialized = false; - } - - // Unpin the region since we no longer care about its contents. - int ashmemFd = mChannel->getAshmemFd(); - result = ashmem_unpin_region(ashmemFd, 0, 0); - if (result < 0) { - ALOGE("channel '%s' publisher ~ Error %d unpinning ashmem fd %d.", - mChannel->getName().string(), result, ashmemFd); - return UNKNOWN_ERROR; - } - - mPinned = false; - } - - mMotionEventSampleDataTail = NULL; - mWasDispatched = false; - return OK; -} - -status_t InputPublisher::publishInputEvent( - int32_t type, - int32_t deviceId, - int32_t source) { - if (mPinned) { - ALOGE("channel '%s' publisher ~ Attempted to publish a new event but publisher has " - "not yet been reset.", mChannel->getName().string()); - return INVALID_OPERATION; - } - - // Pin the region. - // We do not check for ASHMEM_NOT_PURGED because we don't care about the previous - // contents of the buffer so it does not matter whether it was purged in the meantime. - int ashmemFd = mChannel->getAshmemFd(); - int result = ashmem_pin_region(ashmemFd, 0, 0); - if (result < 0) { - ALOGE("channel '%s' publisher ~ Error %d pinning ashmem fd %d.", - mChannel->getName().string(), result, ashmemFd); - return UNKNOWN_ERROR; - } - - mPinned = true; - - result = sem_init(& mSharedMessage->semaphore, 1, 1); - if (result < 0) { - ALOGE("channel '%s' publisher ~ Error %d in sem_init.", - mChannel->getName().string(), errno); - return UNKNOWN_ERROR; - } - - mSemaphoreInitialized = true; - - mSharedMessage->consumed = false; - mSharedMessage->type = type; - mSharedMessage->deviceId = deviceId; - mSharedMessage->source = source; - return OK; } status_t InputPublisher::publishKeyEvent( + uint32_t seq, int32_t deviceId, int32_t source, int32_t action, @@ -337,31 +247,37 @@ status_t InputPublisher::publishKeyEvent( nsecs_t downTime, nsecs_t eventTime) { #if DEBUG_TRANSPORT_ACTIONS - ALOGD("channel '%s' publisher ~ publishKeyEvent: deviceId=%d, source=0x%x, " + ALOGD("channel '%s' publisher ~ publishKeyEvent: seq=%u, deviceId=%d, source=0x%x, " "action=0x%x, flags=0x%x, keyCode=%d, scanCode=%d, metaState=0x%x, repeatCount=%d," "downTime=%lld, eventTime=%lld", - mChannel->getName().string(), + mChannel->getName().string(), seq, deviceId, source, action, flags, keyCode, scanCode, metaState, repeatCount, downTime, eventTime); #endif - status_t result = publishInputEvent(AINPUT_EVENT_TYPE_KEY, deviceId, source); - if (result < 0) { - return result; + if (!seq) { + ALOGE("Attempted to publish a key event with sequence number 0."); + return BAD_VALUE; } - mSharedMessage->key.action = action; - mSharedMessage->key.flags = flags; - mSharedMessage->key.keyCode = keyCode; - mSharedMessage->key.scanCode = scanCode; - mSharedMessage->key.metaState = metaState; - mSharedMessage->key.repeatCount = repeatCount; - mSharedMessage->key.downTime = downTime; - mSharedMessage->key.eventTime = eventTime; - return OK; + InputMessage msg; + msg.header.type = InputMessage::TYPE_KEY; + msg.body.key.seq = seq; + msg.body.key.deviceId = deviceId; + msg.body.key.source = source; + msg.body.key.action = action; + msg.body.key.flags = flags; + msg.body.key.keyCode = keyCode; + msg.body.key.scanCode = scanCode; + msg.body.key.metaState = metaState; + msg.body.key.repeatCount = repeatCount; + msg.body.key.downTime = downTime; + msg.body.key.eventTime = eventTime; + return mChannel->sendMessage(&msg); } status_t InputPublisher::publishMotionEvent( + uint32_t seq, int32_t deviceId, int32_t source, int32_t action, @@ -379,349 +295,663 @@ status_t InputPublisher::publishMotionEvent( const PointerProperties* pointerProperties, const PointerCoords* pointerCoords) { #if DEBUG_TRANSPORT_ACTIONS - ALOGD("channel '%s' publisher ~ publishMotionEvent: deviceId=%d, source=0x%x, " + ALOGD("channel '%s' publisher ~ publishMotionEvent: seq=%u, deviceId=%d, source=0x%x, " "action=0x%x, flags=0x%x, edgeFlags=0x%x, metaState=0x%x, buttonState=0x%x, " "xOffset=%f, yOffset=%f, " "xPrecision=%f, yPrecision=%f, downTime=%lld, eventTime=%lld, " "pointerCount=%d", - mChannel->getName().string(), + mChannel->getName().string(), seq, deviceId, source, action, flags, edgeFlags, metaState, buttonState, xOffset, yOffset, xPrecision, yPrecision, downTime, eventTime, pointerCount); #endif + if (!seq) { + ALOGE("Attempted to publish a motion event with sequence number 0."); + return BAD_VALUE; + } + if (pointerCount > MAX_POINTERS || pointerCount < 1) { ALOGE("channel '%s' publisher ~ Invalid number of pointers provided: %d.", mChannel->getName().string(), pointerCount); return BAD_VALUE; } - status_t result = publishInputEvent(AINPUT_EVENT_TYPE_MOTION, deviceId, source); - if (result < 0) { - return result; - } - - mSharedMessage->motion.action = action; - mSharedMessage->motion.flags = flags; - mSharedMessage->motion.edgeFlags = edgeFlags; - mSharedMessage->motion.metaState = metaState; - mSharedMessage->motion.buttonState = buttonState; - mSharedMessage->motion.xOffset = xOffset; - mSharedMessage->motion.yOffset = yOffset; - mSharedMessage->motion.xPrecision = xPrecision; - mSharedMessage->motion.yPrecision = yPrecision; - mSharedMessage->motion.downTime = downTime; - mSharedMessage->motion.pointerCount = pointerCount; - - mSharedMessage->motion.sampleCount = 1; - mSharedMessage->motion.sampleData[0].eventTime = eventTime; - + InputMessage msg; + msg.header.type = InputMessage::TYPE_MOTION; + msg.body.motion.seq = seq; + msg.body.motion.deviceId = deviceId; + msg.body.motion.source = source; + msg.body.motion.action = action; + msg.body.motion.flags = flags; + msg.body.motion.edgeFlags = edgeFlags; + msg.body.motion.metaState = metaState; + msg.body.motion.buttonState = buttonState; + msg.body.motion.xOffset = xOffset; + msg.body.motion.yOffset = yOffset; + msg.body.motion.xPrecision = xPrecision; + msg.body.motion.yPrecision = yPrecision; + msg.body.motion.downTime = downTime; + msg.body.motion.eventTime = eventTime; + msg.body.motion.pointerCount = pointerCount; for (size_t i = 0; i < pointerCount; i++) { - mSharedMessage->motion.pointerProperties[i].copyFrom(pointerProperties[i]); - mSharedMessage->motion.sampleData[0].coords[i].copyFrom(pointerCoords[i]); + msg.body.motion.pointers[i].properties.copyFrom(pointerProperties[i]); + msg.body.motion.pointers[i].coords.copyFrom(pointerCoords[i]); } - - // Cache essential information about the motion event to ensure that a malicious consumer - // cannot confuse the publisher by modifying the contents of the shared memory buffer while - // it is being updated. - if (action == AMOTION_EVENT_ACTION_MOVE - || action == AMOTION_EVENT_ACTION_HOVER_MOVE) { - mMotionEventPointerCount = pointerCount; - mMotionEventSampleDataStride = InputMessage::sampleDataStride(pointerCount); - mMotionEventSampleDataTail = InputMessage::sampleDataPtrIncrement( - mSharedMessage->motion.sampleData, mMotionEventSampleDataStride); - } else { - mMotionEventSampleDataTail = NULL; - } - return OK; + return mChannel->sendMessage(&msg); } -status_t InputPublisher::appendMotionSample( - nsecs_t eventTime, - const PointerCoords* pointerCoords) { -#if DEBUG_TRANSPORT_ACTIONS - ALOGD("channel '%s' publisher ~ appendMotionSample: eventTime=%lld", - mChannel->getName().string(), eventTime); -#endif - - if (! mPinned || ! mMotionEventSampleDataTail) { - ALOGE("channel '%s' publisher ~ Cannot append motion sample because there is no current " - "AMOTION_EVENT_ACTION_MOVE or AMOTION_EVENT_ACTION_HOVER_MOVE event.", - mChannel->getName().string()); - return INVALID_OPERATION; - } - - InputMessage::SampleData* newTail = InputMessage::sampleDataPtrIncrement( - mMotionEventSampleDataTail, mMotionEventSampleDataStride); - size_t newBytesUsed = reinterpret_cast(newTail) - - reinterpret_cast(mSharedMessage); - - if (newBytesUsed > mAshmemSize) { -#if DEBUG_TRANSPORT_ACTIONS - ALOGD("channel '%s' publisher ~ Cannot append motion sample because the shared memory " - "buffer is full. Buffer size: %d bytes, pointers: %d, samples: %d", - mChannel->getName().string(), - mAshmemSize, mMotionEventPointerCount, mSharedMessage->motion.sampleCount); -#endif - return NO_MEMORY; - } - - int result; - if (mWasDispatched) { - result = sem_trywait(& mSharedMessage->semaphore); - if (result < 0) { - if (errno == EAGAIN) { - // Only possible source of contention is the consumer having consumed (or being in the - // process of consuming) the message and left the semaphore count at 0. -#if DEBUG_TRANSPORT_ACTIONS - ALOGD("channel '%s' publisher ~ Cannot append motion sample because the message has " - "already been consumed.", mChannel->getName().string()); -#endif - return FAILED_TRANSACTION; - } else { - ALOGE("channel '%s' publisher ~ Error %d in sem_trywait.", - mChannel->getName().string(), errno); - return UNKNOWN_ERROR; - } - } - } - - mMotionEventSampleDataTail->eventTime = eventTime; - for (size_t i = 0; i < mMotionEventPointerCount; i++) { - mMotionEventSampleDataTail->coords[i].copyFrom(pointerCoords[i]); - } - mMotionEventSampleDataTail = newTail; - - mSharedMessage->motion.sampleCount += 1; - - if (mWasDispatched) { - result = sem_post(& mSharedMessage->semaphore); - if (result < 0) { - ALOGE("channel '%s' publisher ~ Error %d in sem_post.", - mChannel->getName().string(), errno); - return UNKNOWN_ERROR; - } - } - return OK; -} - -status_t InputPublisher::sendDispatchSignal() { -#if DEBUG_TRANSPORT_ACTIONS - ALOGD("channel '%s' publisher ~ sendDispatchSignal", - mChannel->getName().string()); -#endif - - mWasDispatched = true; - return mChannel->sendSignal(INPUT_SIGNAL_DISPATCH); -} - -status_t InputPublisher::receiveFinishedSignal(bool* outHandled) { +status_t InputPublisher::receiveFinishedSignal(uint32_t* outSeq, bool* outHandled) { #if DEBUG_TRANSPORT_ACTIONS ALOGD("channel '%s' publisher ~ receiveFinishedSignal", mChannel->getName().string()); #endif - char signal; - status_t result = mChannel->receiveSignal(& signal); + InputMessage msg; + status_t result = mChannel->receiveMessage(&msg); if (result) { + *outSeq = 0; *outHandled = false; return result; } - if (signal == INPUT_SIGNAL_FINISHED_HANDLED) { - *outHandled = true; - } else if (signal == INPUT_SIGNAL_FINISHED_UNHANDLED) { - *outHandled = false; - } else { - ALOGE("channel '%s' publisher ~ Received unexpected signal '%c' from consumer", - mChannel->getName().string(), signal); + if (msg.header.type != InputMessage::TYPE_FINISHED) { + ALOGE("channel '%s' publisher ~ Received unexpected message of type %d from consumer", + mChannel->getName().string(), msg.header.type); return UNKNOWN_ERROR; } + *outSeq = msg.body.finished.seq; + *outHandled = msg.body.finished.handled; return OK; } // --- InputConsumer --- InputConsumer::InputConsumer(const sp& channel) : - mChannel(channel), mSharedMessage(NULL) { + mResampleTouch(isTouchResamplingEnabled()), + mChannel(channel), mMsgDeferred(false) { } InputConsumer::~InputConsumer() { - if (mSharedMessage) { - munmap(mSharedMessage, mAshmemSize); - } } -status_t InputConsumer::initialize() { -#if DEBUG_TRANSPORT_ACTIONS - ALOGD("channel '%s' consumer ~ initialize", - mChannel->getName().string()); -#endif - - int ashmemFd = mChannel->getAshmemFd(); - int result = ashmem_get_size_region(ashmemFd); - if (result < 0) { - ALOGE("channel '%s' consumer ~ Error %d getting size of ashmem fd %d.", - mChannel->getName().string(), result, ashmemFd); - return UNKNOWN_ERROR; +bool InputConsumer::isTouchResamplingEnabled() { + char value[PROPERTY_VALUE_MAX]; + int length = property_get("debug.inputconsumer.resample", value, NULL); + if (length > 0) { + if (!strcmp("0", value)) { + return false; + } + if (strcmp("1", value)) { + ALOGD("Unrecognized property value for 'debug.inputconsumer.resample'. " + "Use '1' or '0'."); + } } - - mAshmemSize = (size_t) result; - - mSharedMessage = static_cast(mmap(NULL, mAshmemSize, - PROT_READ | PROT_WRITE, MAP_SHARED, ashmemFd, 0)); - if (! mSharedMessage) { - ALOGE("channel '%s' consumer ~ mmap failed on ashmem fd %d.", - mChannel->getName().string(), ashmemFd); - return NO_MEMORY; - } - - return OK; + return true; } -status_t InputConsumer::consume(InputEventFactoryInterface* factory, InputEvent** outEvent) { +status_t InputConsumer::consume(InputEventFactoryInterface* factory, + bool consumeBatches, nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent) { #if DEBUG_TRANSPORT_ACTIONS - ALOGD("channel '%s' consumer ~ consume", - mChannel->getName().string()); + ALOGD("channel '%s' consumer ~ consume: consumeBatches=%s, frameTime=%lld", + mChannel->getName().string(), consumeBatches ? "true" : "false", frameTime); #endif + *outSeq = 0; *outEvent = NULL; - int ashmemFd = mChannel->getAshmemFd(); - int result = ashmem_pin_region(ashmemFd, 0, 0); - if (result != ASHMEM_NOT_PURGED) { - if (result == ASHMEM_WAS_PURGED) { - ALOGE("channel '%s' consumer ~ Error %d pinning ashmem fd %d because it was purged " - "which probably indicates that the publisher and consumer are out of sync.", - mChannel->getName().string(), result, ashmemFd); - return INVALID_OPERATION; + // Fetch the next input message. + // Loop until an event can be returned or no additional events are received. + while (!*outEvent) { + if (mMsgDeferred) { + // mMsg contains a valid input message from the previous call to consume + // that has not yet been processed. + mMsgDeferred = false; + } else { + // Receive a fresh message. + status_t result = mChannel->receiveMessage(&mMsg); + if (result) { + // Consume the next batched event unless batches are being held for later. + if (consumeBatches || result != WOULD_BLOCK) { + result = consumeBatch(factory, frameTime, outSeq, outEvent); + if (*outEvent) { +#if DEBUG_TRANSPORT_ACTIONS + ALOGD("channel '%s' consumer ~ consumed batch event, seq=%u", + mChannel->getName().string(), *outSeq); +#endif + break; + } + } + return result; + } } - ALOGE("channel '%s' consumer ~ Error %d pinning ashmem fd %d.", - mChannel->getName().string(), result, ashmemFd); - return UNKNOWN_ERROR; + switch (mMsg.header.type) { + case InputMessage::TYPE_KEY: { + KeyEvent* keyEvent = factory->createKeyEvent(); + if (!keyEvent) return NO_MEMORY; + + initializeKeyEvent(keyEvent, &mMsg); + *outSeq = mMsg.body.key.seq; + *outEvent = keyEvent; +#if DEBUG_TRANSPORT_ACTIONS + ALOGD("channel '%s' consumer ~ consumed key event, seq=%u", + mChannel->getName().string(), *outSeq); +#endif + break; + } + + case AINPUT_EVENT_TYPE_MOTION: { + ssize_t batchIndex = findBatch(mMsg.body.motion.deviceId, mMsg.body.motion.source); + if (batchIndex >= 0) { + Batch& batch = mBatches.editItemAt(batchIndex); + if (canAddSample(batch, &mMsg)) { + batch.samples.push(mMsg); +#if DEBUG_TRANSPORT_ACTIONS + ALOGD("channel '%s' consumer ~ appended to batch event", + mChannel->getName().string()); +#endif + break; + } else { + // We cannot append to the batch in progress, so we need to consume + // the previous batch right now and defer the new message until later. + mMsgDeferred = true; + status_t result = consumeSamples(factory, + batch, batch.samples.size(), outSeq, outEvent); + mBatches.removeAt(batchIndex); + if (result) { + return result; + } +#if DEBUG_TRANSPORT_ACTIONS + ALOGD("channel '%s' consumer ~ consumed batch event and " + "deferred current event, seq=%u", + mChannel->getName().string(), *outSeq); +#endif + break; + } + } + + // Start a new batch if needed. + if (mMsg.body.motion.action == AMOTION_EVENT_ACTION_MOVE + || mMsg.body.motion.action == AMOTION_EVENT_ACTION_HOVER_MOVE) { + mBatches.push(); + Batch& batch = mBatches.editTop(); + batch.samples.push(mMsg); +#if DEBUG_TRANSPORT_ACTIONS + ALOGD("channel '%s' consumer ~ started batch event", + mChannel->getName().string()); +#endif + break; + } + + MotionEvent* motionEvent = factory->createMotionEvent(); + if (! motionEvent) return NO_MEMORY; + + updateTouchState(&mMsg); + initializeMotionEvent(motionEvent, &mMsg); + *outSeq = mMsg.body.motion.seq; + *outEvent = motionEvent; +#if DEBUG_TRANSPORT_ACTIONS + ALOGD("channel '%s' consumer ~ consumed motion event, seq=%u", + mChannel->getName().string(), *outSeq); +#endif + break; + } + + default: + ALOGE("channel '%s' consumer ~ Received unexpected message of type %d", + mChannel->getName().string(), mMsg.header.type); + return UNKNOWN_ERROR; + } } - - if (mSharedMessage->consumed) { - ALOGE("channel '%s' consumer ~ The current message has already been consumed.", - mChannel->getName().string()); - return INVALID_OPERATION; - } - - // Acquire but *never release* the semaphore. Contention on the semaphore is used to signal - // to the publisher that the message has been consumed (or is in the process of being - // consumed). Eventually the publisher will reinitialize the semaphore for the next message. - result = sem_wait(& mSharedMessage->semaphore); - if (result < 0) { - ALOGE("channel '%s' consumer ~ Error %d in sem_wait.", - mChannel->getName().string(), errno); - return UNKNOWN_ERROR; - } - - mSharedMessage->consumed = true; - - switch (mSharedMessage->type) { - case AINPUT_EVENT_TYPE_KEY: { - KeyEvent* keyEvent = factory->createKeyEvent(); - if (! keyEvent) return NO_MEMORY; - - populateKeyEvent(keyEvent); - - *outEvent = keyEvent; - break; - } - - case AINPUT_EVENT_TYPE_MOTION: { - MotionEvent* motionEvent = factory->createMotionEvent(); - if (! motionEvent) return NO_MEMORY; - - populateMotionEvent(motionEvent); - - *outEvent = motionEvent; - break; - } - - default: - ALOGE("channel '%s' consumer ~ Received message of unknown type %d", - mChannel->getName().string(), mSharedMessage->type); - return UNKNOWN_ERROR; - } - return OK; } -status_t InputConsumer::sendFinishedSignal(bool handled) { -#if DEBUG_TRANSPORT_ACTIONS - ALOGD("channel '%s' consumer ~ sendFinishedSignal: handled=%d", - mChannel->getName().string(), handled); -#endif +status_t InputConsumer::consumeBatch(InputEventFactoryInterface* factory, + nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent) { + status_t result; + for (size_t i = mBatches.size(); i-- > 0; ) { + Batch& batch = mBatches.editItemAt(i); + if (frameTime < 0) { + result = consumeSamples(factory, batch, batch.samples.size(), + outSeq, outEvent); + mBatches.removeAt(i); + return result; + } - return mChannel->sendSignal(handled - ? INPUT_SIGNAL_FINISHED_HANDLED - : INPUT_SIGNAL_FINISHED_UNHANDLED); -} + nsecs_t sampleTime = frameTime - RESAMPLE_LATENCY; + ssize_t split = findSampleNoLaterThan(batch, sampleTime); + if (split < 0) { + continue; + } -status_t InputConsumer::receiveDispatchSignal() { -#if DEBUG_TRANSPORT_ACTIONS - ALOGD("channel '%s' consumer ~ receiveDispatchSignal", - mChannel->getName().string()); -#endif - - char signal; - status_t result = mChannel->receiveSignal(& signal); - if (result) { + result = consumeSamples(factory, batch, split + 1, outSeq, outEvent); + const InputMessage* next; + if (batch.samples.isEmpty()) { + mBatches.removeAt(i); + next = NULL; + } else { + next = &batch.samples.itemAt(0); + } + if (!result) { + resampleTouchState(sampleTime, static_cast(*outEvent), next); + } return result; } - if (signal != INPUT_SIGNAL_DISPATCH) { - ALOGE("channel '%s' consumer ~ Received unexpected signal '%c' from publisher", - mChannel->getName().string(), signal); - return UNKNOWN_ERROR; + + return WOULD_BLOCK; +} + +status_t InputConsumer::consumeSamples(InputEventFactoryInterface* factory, + Batch& batch, size_t count, uint32_t* outSeq, InputEvent** outEvent) { + MotionEvent* motionEvent = factory->createMotionEvent(); + if (! motionEvent) return NO_MEMORY; + + uint32_t chain = 0; + for (size_t i = 0; i < count; i++) { + InputMessage& msg = batch.samples.editItemAt(i); + updateTouchState(&msg); + if (i) { + SeqChain seqChain; + seqChain.seq = msg.body.motion.seq; + seqChain.chain = chain; + mSeqChains.push(seqChain); + addSample(motionEvent, &msg); + } else { + initializeMotionEvent(motionEvent, &msg); + } + chain = msg.body.motion.seq; } + batch.samples.removeItemsAt(0, count); + + *outSeq = chain; + *outEvent = motionEvent; return OK; } -void InputConsumer::populateKeyEvent(KeyEvent* keyEvent) const { - keyEvent->initialize( - mSharedMessage->deviceId, - mSharedMessage->source, - mSharedMessage->key.action, - mSharedMessage->key.flags, - mSharedMessage->key.keyCode, - mSharedMessage->key.scanCode, - mSharedMessage->key.metaState, - mSharedMessage->key.repeatCount, - mSharedMessage->key.downTime, - mSharedMessage->key.eventTime); +void InputConsumer::updateTouchState(InputMessage* msg) { + if (!mResampleTouch || + !(msg->body.motion.source & AINPUT_SOURCE_CLASS_POINTER)) { + return; + } + + int32_t deviceId = msg->body.motion.deviceId; + int32_t source = msg->body.motion.source; + nsecs_t eventTime = msg->body.motion.eventTime; + + // Update the touch state history to incorporate the new input message. + // If the message is in the past relative to the most recently produced resampled + // touch, then use the resampled time and coordinates instead. + switch (msg->body.motion.action & AMOTION_EVENT_ACTION_MASK) { + case AMOTION_EVENT_ACTION_DOWN: { + ssize_t index = findTouchState(deviceId, source); + if (index < 0) { + mTouchStates.push(); + index = mTouchStates.size() - 1; + } + TouchState& touchState = mTouchStates.editItemAt(index); + touchState.initialize(deviceId, source); + touchState.addHistory(msg); + break; + } + + case AMOTION_EVENT_ACTION_MOVE: { + ssize_t index = findTouchState(deviceId, source); + if (index >= 0) { + TouchState& touchState = mTouchStates.editItemAt(index); + touchState.addHistory(msg); + if (eventTime < touchState.lastResample.eventTime) { + rewriteMessage(touchState, msg); + } else { + touchState.lastResample.idBits.clear(); + } + } + break; + } + + case AMOTION_EVENT_ACTION_POINTER_DOWN: { + ssize_t index = findTouchState(deviceId, source); + if (index >= 0) { + TouchState& touchState = mTouchStates.editItemAt(index); + touchState.lastResample.idBits.clearBit(msg->body.motion.getActionId()); + rewriteMessage(touchState, msg); + } + break; + } + + case AMOTION_EVENT_ACTION_POINTER_UP: { + ssize_t index = findTouchState(deviceId, source); + if (index >= 0) { + TouchState& touchState = mTouchStates.editItemAt(index); + rewriteMessage(touchState, msg); + touchState.lastResample.idBits.clearBit(msg->body.motion.getActionId()); + } + break; + } + + case AMOTION_EVENT_ACTION_SCROLL: { + ssize_t index = findTouchState(deviceId, source); + if (index >= 0) { + const TouchState& touchState = mTouchStates.itemAt(index); + rewriteMessage(touchState, msg); + } + break; + } + + case AMOTION_EVENT_ACTION_UP: + case AMOTION_EVENT_ACTION_CANCEL: { + ssize_t index = findTouchState(deviceId, source); + if (index >= 0) { + const TouchState& touchState = mTouchStates.itemAt(index); + rewriteMessage(touchState, msg); + mTouchStates.removeAt(index); + } + break; + } + } } -void InputConsumer::populateMotionEvent(MotionEvent* motionEvent) const { - motionEvent->initialize( - mSharedMessage->deviceId, - mSharedMessage->source, - mSharedMessage->motion.action, - mSharedMessage->motion.flags, - mSharedMessage->motion.edgeFlags, - mSharedMessage->motion.metaState, - mSharedMessage->motion.buttonState, - mSharedMessage->motion.xOffset, - mSharedMessage->motion.yOffset, - mSharedMessage->motion.xPrecision, - mSharedMessage->motion.yPrecision, - mSharedMessage->motion.downTime, - mSharedMessage->motion.sampleData[0].eventTime, - mSharedMessage->motion.pointerCount, - mSharedMessage->motion.pointerProperties, - mSharedMessage->motion.sampleData[0].coords); - - size_t sampleCount = mSharedMessage->motion.sampleCount; - if (sampleCount > 1) { - InputMessage::SampleData* sampleData = mSharedMessage->motion.sampleData; - size_t sampleDataStride = InputMessage::sampleDataStride( - mSharedMessage->motion.pointerCount); - - while (--sampleCount > 0) { - sampleData = InputMessage::sampleDataPtrIncrement(sampleData, sampleDataStride); - motionEvent->addSample(sampleData->eventTime, sampleData->coords); +void InputConsumer::rewriteMessage(const TouchState& state, InputMessage* msg) { + for (size_t i = 0; i < msg->body.motion.pointerCount; i++) { + uint32_t id = msg->body.motion.pointers[i].properties.id; + if (state.lastResample.idBits.hasBit(id)) { + PointerCoords& msgCoords = msg->body.motion.pointers[i].coords; + const PointerCoords& resampleCoords = state.lastResample.getPointerById(id); +#if DEBUG_RESAMPLING + ALOGD("[%d] - rewrite (%0.3f, %0.3f), old (%0.3f, %0.3f)", id, + resampleCoords.getAxisValue(AMOTION_EVENT_AXIS_X), + resampleCoords.getAxisValue(AMOTION_EVENT_AXIS_Y), + msgCoords.getAxisValue(AMOTION_EVENT_AXIS_X), + msgCoords.getAxisValue(AMOTION_EVENT_AXIS_Y)); +#endif + msgCoords.setAxisValue(AMOTION_EVENT_AXIS_X, resampleCoords.getX()); + msgCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, resampleCoords.getY()); } } } +void InputConsumer::resampleTouchState(nsecs_t sampleTime, MotionEvent* event, + const InputMessage* next) { + if (!mResampleTouch + || !(event->getSource() & AINPUT_SOURCE_CLASS_POINTER) + || event->getAction() != AMOTION_EVENT_ACTION_MOVE) { + return; + } + + ssize_t index = findTouchState(event->getDeviceId(), event->getSource()); + if (index < 0) { +#if DEBUG_RESAMPLING + ALOGD("Not resampled, no touch state for device."); +#endif + return; + } + + TouchState& touchState = mTouchStates.editItemAt(index); + if (touchState.historySize < 1) { +#if DEBUG_RESAMPLING + ALOGD("Not resampled, no history for device."); +#endif + return; + } + + // Ensure that the current sample has all of the pointers that need to be reported. + const History* current = touchState.getHistory(0); + size_t pointerCount = event->getPointerCount(); + for (size_t i = 0; i < pointerCount; i++) { + uint32_t id = event->getPointerId(i); + if (!current->idBits.hasBit(id)) { +#if DEBUG_RESAMPLING + ALOGD("Not resampled, missing id %d", id); +#endif + return; + } + } + + // Find the data to use for resampling. + const History* other; + History future; + float alpha; + if (next) { + // Interpolate between current sample and future sample. + // So current->eventTime <= sampleTime <= future.eventTime. + future.initializeFrom(next); + other = &future; + nsecs_t delta = future.eventTime - current->eventTime; + if (delta < RESAMPLE_MIN_DELTA) { +#if DEBUG_RESAMPLING + ALOGD("Not resampled, delta time is %lld ns.", delta); +#endif + return; + } + alpha = float(sampleTime - current->eventTime) / delta; + } else if (touchState.historySize >= 2) { + // Extrapolate future sample using current sample and past sample. + // So other->eventTime <= current->eventTime <= sampleTime. + other = touchState.getHistory(1); + nsecs_t delta = current->eventTime - other->eventTime; + if (delta < RESAMPLE_MIN_DELTA) { +#if DEBUG_RESAMPLING + ALOGD("Not resampled, delta time is %lld ns.", delta); +#endif + return; + } + nsecs_t maxPredict = current->eventTime + min(delta / 2, RESAMPLE_MAX_PREDICTION); + if (sampleTime > maxPredict) { +#if DEBUG_RESAMPLING + ALOGD("Sample time is too far in the future, adjusting prediction " + "from %lld to %lld ns.", + sampleTime - current->eventTime, maxPredict - current->eventTime); +#endif + sampleTime = maxPredict; + } + alpha = float(current->eventTime - sampleTime) / delta; + } else { +#if DEBUG_RESAMPLING + ALOGD("Not resampled, insufficient data."); +#endif + return; + } + + // Resample touch coordinates. + touchState.lastResample.eventTime = sampleTime; + touchState.lastResample.idBits.clear(); + for (size_t i = 0; i < pointerCount; i++) { + uint32_t id = event->getPointerId(i); + touchState.lastResample.idToIndex[id] = i; + touchState.lastResample.idBits.markBit(id); + PointerCoords& resampledCoords = touchState.lastResample.pointers[i]; + const PointerCoords& currentCoords = current->getPointerById(id); + if (other->idBits.hasBit(id) + && shouldResampleTool(event->getToolType(i))) { + const PointerCoords& otherCoords = other->getPointerById(id); + resampledCoords.copyFrom(currentCoords); + resampledCoords.setAxisValue(AMOTION_EVENT_AXIS_X, + lerp(currentCoords.getX(), otherCoords.getX(), alpha)); + resampledCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, + lerp(currentCoords.getY(), otherCoords.getY(), alpha)); +#if DEBUG_RESAMPLING + ALOGD("[%d] - out (%0.3f, %0.3f), cur (%0.3f, %0.3f), " + "other (%0.3f, %0.3f), alpha %0.3f", + id, resampledCoords.getX(), resampledCoords.getY(), + currentCoords.getX(), currentCoords.getY(), + otherCoords.getX(), otherCoords.getY(), + alpha); +#endif + } else { + resampledCoords.copyFrom(currentCoords); +#if DEBUG_RESAMPLING + ALOGD("[%d] - out (%0.3f, %0.3f), cur (%0.3f, %0.3f)", + id, resampledCoords.getX(), resampledCoords.getY(), + currentCoords.getX(), currentCoords.getY()); +#endif + } + } + + event->addSample(sampleTime, touchState.lastResample.pointers); +} + +bool InputConsumer::shouldResampleTool(int32_t toolType) { + return toolType == AMOTION_EVENT_TOOL_TYPE_FINGER + || toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN; +} + +status_t InputConsumer::sendFinishedSignal(uint32_t seq, bool handled) { +#if DEBUG_TRANSPORT_ACTIONS + ALOGD("channel '%s' consumer ~ sendFinishedSignal: seq=%u, handled=%s", + mChannel->getName().string(), seq, handled ? "true" : "false"); +#endif + + if (!seq) { + ALOGE("Attempted to send a finished signal with sequence number 0."); + return BAD_VALUE; + } + + // Send finished signals for the batch sequence chain first. + size_t seqChainCount = mSeqChains.size(); + if (seqChainCount) { + uint32_t currentSeq = seq; + uint32_t chainSeqs[seqChainCount]; + size_t chainIndex = 0; + for (size_t i = seqChainCount; i-- > 0; ) { + const SeqChain& seqChain = mSeqChains.itemAt(i); + if (seqChain.seq == currentSeq) { + currentSeq = seqChain.chain; + chainSeqs[chainIndex++] = currentSeq; + mSeqChains.removeAt(i); + } + } + status_t status = OK; + while (!status && chainIndex-- > 0) { + status = sendUnchainedFinishedSignal(chainSeqs[chainIndex], handled); + } + if (status) { + // An error occurred so at least one signal was not sent, reconstruct the chain. + do { + SeqChain seqChain; + seqChain.seq = chainIndex != 0 ? chainSeqs[chainIndex - 1] : seq; + seqChain.chain = chainSeqs[chainIndex]; + mSeqChains.push(seqChain); + } while (chainIndex-- > 0); + return status; + } + } + + // Send finished signal for the last message in the batch. + return sendUnchainedFinishedSignal(seq, handled); +} + +status_t InputConsumer::sendUnchainedFinishedSignal(uint32_t seq, bool handled) { + InputMessage msg; + msg.header.type = InputMessage::TYPE_FINISHED; + msg.body.finished.seq = seq; + msg.body.finished.handled = handled; + return mChannel->sendMessage(&msg); +} + +bool InputConsumer::hasDeferredEvent() const { + return mMsgDeferred; +} + +bool InputConsumer::hasPendingBatch() const { + return !mBatches.isEmpty(); +} + +ssize_t InputConsumer::findBatch(int32_t deviceId, int32_t source) const { + for (size_t i = 0; i < mBatches.size(); i++) { + const Batch& batch = mBatches.itemAt(i); + const InputMessage& head = batch.samples.itemAt(0); + if (head.body.motion.deviceId == deviceId && head.body.motion.source == source) { + return i; + } + } + return -1; +} + +ssize_t InputConsumer::findTouchState(int32_t deviceId, int32_t source) const { + for (size_t i = 0; i < mTouchStates.size(); i++) { + const TouchState& touchState = mTouchStates.itemAt(i); + if (touchState.deviceId == deviceId && touchState.source == source) { + return i; + } + } + return -1; +} + +void InputConsumer::initializeKeyEvent(KeyEvent* event, const InputMessage* msg) { + event->initialize( + msg->body.key.deviceId, + msg->body.key.source, + msg->body.key.action, + msg->body.key.flags, + msg->body.key.keyCode, + msg->body.key.scanCode, + msg->body.key.metaState, + msg->body.key.repeatCount, + msg->body.key.downTime, + msg->body.key.eventTime); +} + +void InputConsumer::initializeMotionEvent(MotionEvent* event, const InputMessage* msg) { + size_t pointerCount = msg->body.motion.pointerCount; + PointerProperties pointerProperties[pointerCount]; + PointerCoords pointerCoords[pointerCount]; + for (size_t i = 0; i < pointerCount; i++) { + pointerProperties[i].copyFrom(msg->body.motion.pointers[i].properties); + pointerCoords[i].copyFrom(msg->body.motion.pointers[i].coords); + } + + event->initialize( + msg->body.motion.deviceId, + msg->body.motion.source, + msg->body.motion.action, + msg->body.motion.flags, + msg->body.motion.edgeFlags, + msg->body.motion.metaState, + msg->body.motion.buttonState, + msg->body.motion.xOffset, + msg->body.motion.yOffset, + msg->body.motion.xPrecision, + msg->body.motion.yPrecision, + msg->body.motion.downTime, + msg->body.motion.eventTime, + pointerCount, + pointerProperties, + pointerCoords); +} + +void InputConsumer::addSample(MotionEvent* event, const InputMessage* msg) { + size_t pointerCount = msg->body.motion.pointerCount; + PointerCoords pointerCoords[pointerCount]; + for (size_t i = 0; i < pointerCount; i++) { + pointerCoords[i].copyFrom(msg->body.motion.pointers[i].coords); + } + + event->setMetaState(event->getMetaState() | msg->body.motion.metaState); + event->addSample(msg->body.motion.eventTime, pointerCoords); +} + +bool InputConsumer::canAddSample(const Batch& batch, const InputMessage *msg) { + const InputMessage& head = batch.samples.itemAt(0); + size_t pointerCount = msg->body.motion.pointerCount; + if (head.body.motion.pointerCount != pointerCount + || head.body.motion.action != msg->body.motion.action) { + return false; + } + for (size_t i = 0; i < pointerCount; i++) { + if (head.body.motion.pointers[i].properties + != msg->body.motion.pointers[i].properties) { + return false; + } + } + return true; +} + +ssize_t InputConsumer::findSampleNoLaterThan(const Batch& batch, nsecs_t time) { + size_t numSamples = batch.samples.size(); + size_t index = 0; + while (index < numSamples + && batch.samples.itemAt(index).body.motion.eventTime <= time) { + index += 1; + } + return ssize_t(index) - 1; +} + } // namespace android diff --git a/widget/gonk/libui/InputTransport.h b/widget/gonk/libui/InputTransport.h index 7faeaf88e5c8..66ef2850a3c7 100644 --- a/widget/gonk/libui/InputTransport.h +++ b/widget/gonk/libui/InputTransport.h @@ -14,114 +14,50 @@ * limitations under the License. */ -#ifndef _UI_INPUT_TRANSPORT_H -#define _UI_INPUT_TRANSPORT_H +#ifndef _ANDROIDFW_INPUT_TRANSPORT_H +#define _ANDROIDFW_INPUT_TRANSPORT_H /** * Native input transport. * - * Uses anonymous shared memory as a whiteboard for sending input events from an - * InputPublisher to an InputConsumer and ensuring appropriate synchronization. - * One interesting feature is that published events can be updated in place as long as they - * have not yet been consumed. + * The InputChannel provides a mechanism for exchanging InputMessage structures across processes. * - * The InputPublisher and InputConsumer only take care of transferring event data - * over an InputChannel and sending synchronization signals. The InputDispatcher and InputQueue - * build on these abstractions to add multiplexing and queueing. + * The InputPublisher and InputConsumer each handle one end-point of an input channel. + * The InputPublisher is used by the input dispatcher to send events to the application. + * The InputConsumer is used by the application to receive events from the input dispatcher. */ -#include #include "Input.h" #include -#include "Timers.h" +#include #include -#include "String8.h" +#include +#include +#include namespace android { /* - * An input channel consists of a shared memory buffer and a pair of pipes - * used to send input messages from an InputPublisher to an InputConsumer - * across processes. Each channel has a descriptive name for debugging purposes. - * - * Each endpoint has its own InputChannel object that specifies its own file descriptors. - * - * The input channel is closed when all references to it are released. - */ -class InputChannel : public RefBase { -protected: - virtual ~InputChannel(); - -public: - InputChannel(const String8& name, int32_t ashmemFd, int32_t receivePipeFd, - int32_t sendPipeFd); - - /* Creates a pair of input channels and their underlying shared memory buffers - * and pipes. - * - * Returns OK on success. - */ - static status_t openInputChannelPair(const String8& name, - sp& outServerChannel, sp& outClientChannel); - - inline String8 getName() const { return mName; } - inline int32_t getAshmemFd() const { return mAshmemFd; } - inline int32_t getReceivePipeFd() const { return mReceivePipeFd; } - inline int32_t getSendPipeFd() const { return mSendPipeFd; } - - /* Sends a signal to the other endpoint. - * - * Returns OK on success. - * Returns DEAD_OBJECT if the channel's peer has been closed. - * Other errors probably indicate that the channel is broken. - */ - status_t sendSignal(char signal); - - /* Receives a signal send by the other endpoint. - * (Should only call this after poll() indicates that the receivePipeFd has available input.) - * - * Returns OK on success. - * Returns WOULD_BLOCK if there is no signal present. - * Returns DEAD_OBJECT if the channel's peer has been closed. - * Other errors probably indicate that the channel is broken. - */ - status_t receiveSignal(char* outSignal); - -private: - String8 mName; - int32_t mAshmemFd; - int32_t mReceivePipeFd; - int32_t mSendPipeFd; -}; - -/* - * Private intermediate representation of input events as messages written into an - * ashmem buffer. + * Intermediate representation used to send input events and related signals. */ struct InputMessage { - /* Semaphore count is set to 1 when the message is published. - * It becomes 0 transiently while the publisher updates the message. - * It becomes 0 permanently when the consumer consumes the message. - */ - sem_t semaphore; - - /* Initialized to false by the publisher. - * Set to true by the consumer when it consumes the message. - */ - bool consumed; - - int32_t type; - - struct SampleData { - nsecs_t eventTime; - PointerCoords coords[0]; // variable length + enum { + TYPE_KEY = 1, + TYPE_MOTION = 2, + TYPE_FINISHED = 3, }; - int32_t deviceId; - int32_t source; + struct Header { + uint32_t type; + uint32_t padding; // 8 byte alignment for the body that follows + } header; - union { - struct { + union Body { + struct Key { + uint32_t seq; + nsecs_t eventTime; + int32_t deviceId; + int32_t source; int32_t action; int32_t flags; int32_t keyCode; @@ -129,10 +65,17 @@ struct InputMessage { int32_t metaState; int32_t repeatCount; nsecs_t downTime; - nsecs_t eventTime; + + inline size_t size() const { + return sizeof(Key); + } } key; - struct { + struct Motion { + uint32_t seq; + nsecs_t eventTime; + int32_t deviceId; + int32_t source; int32_t action; int32_t flags; int32_t metaState; @@ -144,28 +87,97 @@ struct InputMessage { float xPrecision; float yPrecision; size_t pointerCount; - PointerProperties pointerProperties[MAX_POINTERS]; - size_t sampleCount; - SampleData sampleData[0]; // variable length + struct Pointer { + PointerProperties properties; + PointerCoords coords; + } pointers[MAX_POINTERS]; + + int32_t getActionId() const { + uint32_t index = (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) + >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT; + return pointers[index].properties.id; + } + + inline size_t size() const { + return sizeof(Motion) - sizeof(Pointer) * MAX_POINTERS + + sizeof(Pointer) * pointerCount; + } } motion; - }; - /* Gets the number of bytes to add to step to the next SampleData object in a motion - * event message for a given number of pointers. - */ - static inline size_t sampleDataStride(size_t pointerCount) { - return sizeof(InputMessage::SampleData) + pointerCount * sizeof(PointerCoords); - } + struct Finished { + uint32_t seq; + bool handled; - /* Adds the SampleData stride to the given pointer. */ - static inline SampleData* sampleDataPtrIncrement(SampleData* ptr, size_t stride) { - return reinterpret_cast(reinterpret_cast(ptr) + stride); - } + inline size_t size() const { + return sizeof(Finished); + } + } finished; + } body; + + bool isValid(size_t actualSize) const; + size_t size() const; }; /* - * Publishes input events to an anonymous shared memory buffer. - * Uses atomic operations to coordinate shared access with a single concurrent consumer. + * An input channel consists of a local unix domain socket used to send and receive + * input messages across processes. Each channel has a descriptive name for debugging purposes. + * + * Each endpoint has its own InputChannel object that specifies its file descriptor. + * + * The input channel is closed when all references to it are released. + */ +class InputChannel : public RefBase { +protected: + virtual ~InputChannel(); + +public: + InputChannel(const String8& name, int fd); + + /* Creates a pair of input channels. + * + * Returns OK on success. + */ + static status_t openInputChannelPair(const String8& name, + sp& outServerChannel, sp& outClientChannel); + + inline String8 getName() const { return mName; } + inline int getFd() const { return mFd; } + + /* Sends a message to the other endpoint. + * + * If the channel is full then the message is guaranteed not to have been sent at all. + * Try again after the consumer has sent a finished signal indicating that it has + * consumed some of the pending messages from the channel. + * + * Returns OK on success. + * Returns WOULD_BLOCK if the channel is full. + * Returns DEAD_OBJECT if the channel's peer has been closed. + * Other errors probably indicate that the channel is broken. + */ + status_t sendMessage(const InputMessage* msg); + + /* Receives a message sent by the other endpoint. + * + * If there is no message present, try again after poll() indicates that the fd + * is readable. + * + * Returns OK on success. + * Returns WOULD_BLOCK if there is no message present. + * Returns DEAD_OBJECT if the channel's peer has been closed. + * Other errors probably indicate that the channel is broken. + */ + status_t receiveMessage(InputMessage* msg); + + /* Returns a new object that has a duplicate of this channel's fd. */ + sp dup() const; + +private: + String8 mName; + int mFd; +}; + +/* + * Publishes input events to an input channel. */ class InputPublisher { public: @@ -178,26 +190,16 @@ public: /* Gets the underlying input channel. */ inline sp getChannel() { return mChannel; } - /* Prepares the publisher for use. Must be called before it is used. - * Returns OK on success. - * - * This method implicitly calls reset(). */ - status_t initialize(); - - /* Resets the publisher to its initial state and unpins its ashmem buffer. - * Returns OK on success. - * - * Should be called after an event has been consumed to release resources used by the - * publisher until the next event is ready to be published. - */ - status_t reset(); - - /* Publishes a key event to the ashmem buffer. + /* Publishes a key event to the input channel. * * Returns OK on success. - * Returns INVALID_OPERATION if the publisher has not been reset. + * Returns WOULD_BLOCK if the channel is full. + * Returns DEAD_OBJECT if the channel's peer has been closed. + * Returns BAD_VALUE if seq is 0. + * Other errors probably indicate that the channel is broken. */ status_t publishKeyEvent( + uint32_t seq, int32_t deviceId, int32_t source, int32_t action, @@ -209,13 +211,16 @@ public: nsecs_t downTime, nsecs_t eventTime); - /* Publishes a motion event to the ashmem buffer. + /* Publishes a motion event to the input channel. * * Returns OK on success. - * Returns INVALID_OPERATION if the publisher has not been reset. - * Returns BAD_VALUE if pointerCount is less than 1 or greater than MAX_POINTERS. + * Returns WOULD_BLOCK if the channel is full. + * Returns DEAD_OBJECT if the channel's peer has been closed. + * Returns BAD_VALUE if seq is 0 or if pointerCount is less than 1 or greater than MAX_POINTERS. + * Other errors probably indicate that the channel is broken. */ status_t publishMotionEvent( + uint32_t seq, int32_t deviceId, int32_t source, int32_t action, @@ -233,55 +238,25 @@ public: const PointerProperties* pointerProperties, const PointerCoords* pointerCoords); - /* Appends a motion sample to a motion event unless already consumed. - * - * Returns OK on success. - * Returns INVALID_OPERATION if the current event is not a AMOTION_EVENT_ACTION_MOVE event. - * Returns FAILED_TRANSACTION if the current event has already been consumed. - * Returns NO_MEMORY if the buffer is full and no additional samples can be added. - */ - status_t appendMotionSample( - nsecs_t eventTime, - const PointerCoords* pointerCoords); - - /* Sends a dispatch signal to the consumer to inform it that a new message is available. - * - * Returns OK on success. - * Errors probably indicate that the channel is broken. - */ - status_t sendDispatchSignal(); - /* Receives the finished signal from the consumer in reply to the original dispatch signal. - * Returns whether the consumer handled the message. + * If a signal was received, returns the message sequence number, + * and whether the consumer handled the message. + * + * The returned sequence number is never 0 unless the operation failed. * * Returns OK on success. * Returns WOULD_BLOCK if there is no signal present. + * Returns DEAD_OBJECT if the channel's peer has been closed. * Other errors probably indicate that the channel is broken. */ - status_t receiveFinishedSignal(bool* outHandled); + status_t receiveFinishedSignal(uint32_t* outSeq, bool* outHandled); private: sp mChannel; - - size_t mAshmemSize; - InputMessage* mSharedMessage; - bool mPinned; - bool mSemaphoreInitialized; - bool mWasDispatched; - - size_t mMotionEventPointerCount; - InputMessage::SampleData* mMotionEventSampleDataTail; - size_t mMotionEventSampleDataStride; - - status_t publishInputEvent( - int32_t type, - int32_t deviceId, - int32_t source); }; /* - * Consumes input events from an anonymous shared memory buffer. - * Uses atomic operations to coordinate shared access with a single concurrent publisher. + * Consumes input events from an input channel. */ class InputConsumer { public: @@ -294,45 +269,175 @@ public: /* Gets the underlying input channel. */ inline sp getChannel() { return mChannel; } - /* Prepares the consumer for use. Must be called before it is used. */ - status_t initialize(); - - /* Consumes the input event in the buffer and copies its contents into + /* Consumes an input event from the input channel and copies its contents into * an InputEvent object created using the specified factory. - * This operation will block if the publisher is updating the event. + * + * Tries to combine a series of move events into larger batches whenever possible. + * + * If consumeBatches is false, then defers consuming pending batched events if it + * is possible for additional samples to be added to them later. Call hasPendingBatch() + * to determine whether a pending batch is available to be consumed. + * + * If consumeBatches is true, then events are still batched but they are consumed + * immediately as soon as the input channel is exhausted. + * + * The frameTime parameter specifies the time when the current display frame started + * rendering in the CLOCK_MONOTONIC time base, or -1 if unknown. + * + * The returned sequence number is never 0 unless the operation failed. * * Returns OK on success. - * Returns INVALID_OPERATION if there is no currently published event. + * Returns WOULD_BLOCK if there is no event present. + * Returns DEAD_OBJECT if the channel's peer has been closed. * Returns NO_MEMORY if the event could not be created. - */ - status_t consume(InputEventFactoryInterface* factory, InputEvent** outEvent); - - /* Sends a finished signal to the publisher to inform it that the current message is - * finished processing and specifies whether the message was handled by the consumer. - * - * Returns OK on success. - * Errors probably indicate that the channel is broken. - */ - status_t sendFinishedSignal(bool handled); - - /* Receives the dispatched signal from the publisher. - * - * Returns OK on success. - * Returns WOULD_BLOCK if there is no signal present. * Other errors probably indicate that the channel is broken. */ - status_t receiveDispatchSignal(); + status_t consume(InputEventFactoryInterface* factory, bool consumeBatches, + nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent); + + /* Sends a finished signal to the publisher to inform it that the message + * with the specified sequence number has finished being process and whether + * the message was handled by the consumer. + * + * Returns OK on success. + * Returns BAD_VALUE if seq is 0. + * Other errors probably indicate that the channel is broken. + */ + status_t sendFinishedSignal(uint32_t seq, bool handled); + + /* Returns true if there is a deferred event waiting. + * + * Should be called after calling consume() to determine whether the consumer + * has a deferred event to be processed. Deferred events are somewhat special in + * that they have already been removed from the input channel. If the input channel + * becomes empty, the client may need to do extra work to ensure that it processes + * the deferred event despite the fact that the input channel's file descriptor + * is not readable. + * + * One option is simply to call consume() in a loop until it returns WOULD_BLOCK. + * This guarantees that all deferred events will be processed. + * + * Alternately, the caller can call hasDeferredEvent() to determine whether there is + * a deferred event waiting and then ensure that its event loop wakes up at least + * one more time to consume the deferred event. + */ + bool hasDeferredEvent() const; + + /* Returns true if there is a pending batch. + * + * Should be called after calling consume() with consumeBatches == false to determine + * whether consume() should be called again later on with consumeBatches == true. + */ + bool hasPendingBatch() const; private: + // True if touch resampling is enabled. + const bool mResampleTouch; + + // The input channel. sp mChannel; - size_t mAshmemSize; - InputMessage* mSharedMessage; + // The current input message. + InputMessage mMsg; - void populateKeyEvent(KeyEvent* keyEvent) const; - void populateMotionEvent(MotionEvent* motionEvent) const; + // True if mMsg contains a valid input message that was deferred from the previous + // call to consume and that still needs to be handled. + bool mMsgDeferred; + + // Batched motion events per device and source. + struct Batch { + Vector samples; + }; + Vector mBatches; + + // Touch state per device and source, only for sources of class pointer. + struct History { + nsecs_t eventTime; + BitSet32 idBits; + int32_t idToIndex[MAX_POINTER_ID + 1]; + PointerCoords pointers[MAX_POINTERS]; + + void initializeFrom(const InputMessage* msg) { + eventTime = msg->body.motion.eventTime; + idBits.clear(); + for (size_t i = 0; i < msg->body.motion.pointerCount; i++) { + uint32_t id = msg->body.motion.pointers[i].properties.id; + idBits.markBit(id); + idToIndex[id] = i; + pointers[i].copyFrom(msg->body.motion.pointers[i].coords); + } + } + + const PointerCoords& getPointerById(uint32_t id) const { + return pointers[idToIndex[id]]; + } + }; + struct TouchState { + int32_t deviceId; + int32_t source; + size_t historyCurrent; + size_t historySize; + History history[2]; + History lastResample; + + void initialize(int32_t deviceId, int32_t source) { + this->deviceId = deviceId; + this->source = source; + historyCurrent = 0; + historySize = 0; + lastResample.eventTime = 0; + lastResample.idBits.clear(); + } + + void addHistory(const InputMessage* msg) { + historyCurrent ^= 1; + if (historySize < 2) { + historySize += 1; + } + history[historyCurrent].initializeFrom(msg); + } + + const History* getHistory(size_t index) const { + return &history[(historyCurrent + index) & 1]; + } + }; + Vector mTouchStates; + + // Chain of batched sequence numbers. When multiple input messages are combined into + // a batch, we append a record here that associates the last sequence number in the + // batch with the previous one. When the finished signal is sent, we traverse the + // chain to individually finish all input messages that were part of the batch. + struct SeqChain { + uint32_t seq; // sequence number of batched input message + uint32_t chain; // sequence number of previous batched input message + }; + Vector mSeqChains; + + status_t consumeBatch(InputEventFactoryInterface* factory, + nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent); + status_t consumeSamples(InputEventFactoryInterface* factory, + Batch& batch, size_t count, uint32_t* outSeq, InputEvent** outEvent); + + void updateTouchState(InputMessage* msg); + void rewriteMessage(const TouchState& state, InputMessage* msg); + void resampleTouchState(nsecs_t frameTime, MotionEvent* event, + const InputMessage *next); + + ssize_t findBatch(int32_t deviceId, int32_t source) const; + ssize_t findTouchState(int32_t deviceId, int32_t source) const; + + status_t sendUnchainedFinishedSignal(uint32_t seq, bool handled); + + static void initializeKeyEvent(KeyEvent* event, const InputMessage* msg); + static void initializeMotionEvent(MotionEvent* event, const InputMessage* msg); + static void addSample(MotionEvent* event, const InputMessage* msg); + static bool canAddSample(const Batch& batch, const InputMessage* msg); + static ssize_t findSampleNoLaterThan(const Batch& batch, nsecs_t time); + static bool shouldResampleTool(int32_t toolType); + + static bool isTouchResamplingEnabled(); }; } // namespace android -#endif // _UI_INPUT_TRANSPORT_H +#endif // _ANDROIDFW_INPUT_TRANSPORT_H diff --git a/widget/gonk/libui/InputWindow.cpp b/widget/gonk/libui/InputWindow.cpp index 81509b98f6fa..3aea445a0b3f 100644 --- a/widget/gonk/libui/InputWindow.cpp +++ b/widget/gonk/libui/InputWindow.cpp @@ -25,11 +25,7 @@ namespace android { // --- InputWindowInfo --- bool InputWindowInfo::touchableRegionContainsPoint(int32_t x, int32_t y) const { -#ifdef HAVE_ANDROID_OS return touchableRegion.contains(x, y); -#else - return false; -#endif } bool InputWindowInfo::frameContainsPoint(int32_t x, int32_t y) const { diff --git a/widget/gonk/libui/InputWindow.h b/widget/gonk/libui/InputWindow.h index 5361cc86f040..cce5fd4fedbd 100644 --- a/widget/gonk/libui/InputWindow.h +++ b/widget/gonk/libui/InputWindow.h @@ -20,10 +20,10 @@ #include "Input.h" #include "InputTransport.h" #include -#include "Timers.h" -#include "String8.h" +#include +#include -#include "SkRegion.h" +#include #include "InputApplication.h" @@ -110,6 +110,8 @@ struct InputWindowInfo { enum { INPUT_FEATURE_DISABLE_TOUCH_PAD_GESTURES = 0x00000001, + INPUT_FEATURE_NO_INPUT_CHANNEL = 0x00000002, + INPUT_FEATURE_DISABLE_USER_ACTIVITY = 0x00000004, }; sp inputChannel; @@ -122,9 +124,7 @@ struct InputWindowInfo { int32_t frameRight; int32_t frameBottom; float scaleFactor; -#ifdef HAVE_ANDROID_OS SkRegion touchableRegion; -#endif bool visible; bool canReceiveKeys; bool hasFocus; @@ -134,6 +134,7 @@ struct InputWindowInfo { int32_t ownerPid; int32_t ownerUid; int32_t inputFeatures; + int32_t displayId; bool touchableRegionContainsPoint(int32_t x, int32_t y) const; bool frameContainsPoint(int32_t x, int32_t y) const; diff --git a/widget/gonk/libui/KeyCharacterMap.cpp b/widget/gonk/libui/KeyCharacterMap.cpp index bd18c5f3d6a6..cec0666ce72e 100644 --- a/widget/gonk/libui/KeyCharacterMap.cpp +++ b/widget/gonk/libui/KeyCharacterMap.cpp @@ -15,16 +15,21 @@ */ #define LOG_TAG "KeyCharacterMap" +#include "cutils_log.h" #include #include -#include "utils_Log.h" #include "android_keycodes.h" #include "Keyboard.h" #include "KeyCharacterMap.h" + +#if HAVE_ANDROID_OS +#include +#endif + #include #include "Tokenizer.h" -#include "Timers.h" +#include // Enables debug output for the parser. #define DEBUG_PARSER 0 @@ -78,10 +83,20 @@ static String8 toString(const char16_t* chars, size_t numChars) { // --- KeyCharacterMap --- +sp KeyCharacterMap::sEmpty = new KeyCharacterMap(); + KeyCharacterMap::KeyCharacterMap() : mType(KEYBOARD_TYPE_UNKNOWN) { } +KeyCharacterMap::KeyCharacterMap(const KeyCharacterMap& other) : + RefBase(), mType(other.mType), mKeysByScanCode(other.mKeysByScanCode), + mKeysByUsageCode(other.mKeysByUsageCode) { + for (size_t i = 0; i < other.mKeys.size(); i++) { + mKeys.add(other.mKeys.keyAt(i), new Key(*other.mKeys.valueAt(i))); + } +} + KeyCharacterMap::~KeyCharacterMap() { for (size_t i = 0; i < mKeys.size(); i++) { Key* key = mKeys.editValueAt(i); @@ -89,41 +104,100 @@ KeyCharacterMap::~KeyCharacterMap() { } } -status_t KeyCharacterMap::load(const String8& filename, KeyCharacterMap** outMap) { - *outMap = NULL; +status_t KeyCharacterMap::load(const String8& filename, + Format format, sp* outMap) { + outMap->clear(); Tokenizer* tokenizer; status_t status = Tokenizer::open(filename, &tokenizer); if (status) { ALOGE("Error %d opening key character map file %s.", status, filename.string()); } else { - KeyCharacterMap* map = new KeyCharacterMap(); - if (!map) { - ALOGE("Error allocating key character map."); - status = NO_MEMORY; - } else { -#if DEBUG_PARSER_PERFORMANCE - nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC); -#endif - Parser parser(map, tokenizer); - status = parser.parse(); -#if DEBUG_PARSER_PERFORMANCE - nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime; - ALOGD("Parsed key character map file '%s' %d lines in %0.3fms.", - tokenizer->getFilename().string(), tokenizer->getLineNumber(), - elapsedTime / 1000000.0); -#endif - if (status) { - delete map; - } else { - *outMap = map; - } - } + status = load(tokenizer, format, outMap); delete tokenizer; } return status; } +status_t KeyCharacterMap::loadContents(const String8& filename, const char* contents, + Format format, sp* outMap) { + outMap->clear(); + + Tokenizer* tokenizer; + status_t status = Tokenizer::fromContents(filename, contents, &tokenizer); + if (status) { + ALOGE("Error %d opening key character map.", status); + } else { + status = load(tokenizer, format, outMap); + delete tokenizer; + } + return status; +} + +status_t KeyCharacterMap::load(Tokenizer* tokenizer, + Format format, sp* outMap) { + status_t status = OK; + sp map = new KeyCharacterMap(); + if (!map.get()) { + ALOGE("Error allocating key character map."); + status = NO_MEMORY; + } else { +#if DEBUG_PARSER_PERFORMANCE + nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC); +#endif + Parser parser(map.get(), tokenizer, format); + status = parser.parse(); +#if DEBUG_PARSER_PERFORMANCE + nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime; + ALOGD("Parsed key character map file '%s' %d lines in %0.3fms.", + tokenizer->getFilename().string(), tokenizer->getLineNumber(), + elapsedTime / 1000000.0); +#endif + if (!status) { + *outMap = map; + } + } + return status; +} + +sp KeyCharacterMap::combine(const sp& base, + const sp& overlay) { + if (overlay == NULL) { + return base; + } + if (base == NULL) { + return overlay; + } + + sp map = new KeyCharacterMap(*base.get()); + for (size_t i = 0; i < overlay->mKeys.size(); i++) { + int32_t keyCode = overlay->mKeys.keyAt(i); + Key* key = overlay->mKeys.valueAt(i); + ssize_t oldIndex = map->mKeys.indexOfKey(keyCode); + if (oldIndex >= 0) { + delete map->mKeys.valueAt(oldIndex); + map->mKeys.editValueAt(oldIndex) = new Key(*key); + } else { + map->mKeys.add(keyCode, new Key(*key)); + } + } + + for (size_t i = 0; i < overlay->mKeysByScanCode.size(); i++) { + map->mKeysByScanCode.replaceValueFor(overlay->mKeysByScanCode.keyAt(i), + overlay->mKeysByScanCode.valueAt(i)); + } + + for (size_t i = 0; i < overlay->mKeysByUsageCode.size(); i++) { + map->mKeysByUsageCode.replaceValueFor(overlay->mKeysByUsageCode.keyAt(i), + overlay->mKeysByUsageCode.valueAt(i)); + } + return map; +} + +sp KeyCharacterMap::empty() { + return sEmpty; +} + int32_t KeyCharacterMap::getKeyboardType() const { return mType; } @@ -252,6 +326,37 @@ bool KeyCharacterMap::getEvents(int32_t deviceId, const char16_t* chars, size_t return true; } +status_t KeyCharacterMap::mapKey(int32_t scanCode, int32_t usageCode, int32_t* outKeyCode) const { + if (usageCode) { + ssize_t index = mKeysByUsageCode.indexOfKey(usageCode); + if (index >= 0) { +#if DEBUG_MAPPING + ALOGD("mapKey: scanCode=%d, usageCode=0x%08x ~ Result keyCode=%d.", + scanCode, usageCode, *outKeyCode); +#endif + *outKeyCode = mKeysByUsageCode.valueAt(index); + return OK; + } + } + if (scanCode) { + ssize_t index = mKeysByScanCode.indexOfKey(scanCode); + if (index >= 0) { +#if DEBUG_MAPPING + ALOGD("mapKey: scanCode=%d, usageCode=0x%08x ~ Result keyCode=%d.", + scanCode, usageCode, *outKeyCode); +#endif + *outKeyCode = mKeysByScanCode.valueAt(index); + return OK; + } + } + +#if DEBUG_MAPPING + ALOGD("mapKey: scanCode=%d, usageCode=0x%08x ~ Failed.", scanCode, usageCode); +#endif + *outKeyCode = AKEYCODE_UNKNOWN; + return NAME_NOT_FOUND; +} + bool KeyCharacterMap::getKey(int32_t keyCode, const Key** outKey) const { ssize_t index = mKeys.indexOfKey(keyCode); if (index >= 0) { @@ -267,7 +372,7 @@ bool KeyCharacterMap::getKeyBehavior(int32_t keyCode, int32_t metaState, if (getKey(keyCode, &key)) { const Behavior* behavior = key->firstBehavior; while (behavior) { - if ((behavior->metaState & metaState) == behavior->metaState) { + if (matchesMetaState(metaState, behavior->metaState)) { *outKey = key; *outBehavior = behavior; return true; @@ -278,6 +383,37 @@ bool KeyCharacterMap::getKeyBehavior(int32_t keyCode, int32_t metaState, return false; } +bool KeyCharacterMap::matchesMetaState(int32_t eventMetaState, int32_t behaviorMetaState) { + // Behavior must have at least the set of meta states specified. + // And if the key event has CTRL, ALT or META then the behavior must exactly + // match those, taking into account that a behavior can specify that it handles + // one, both or either of a left/right modifier pair. + if ((eventMetaState & behaviorMetaState) == behaviorMetaState) { + const int32_t EXACT_META_STATES = + AMETA_CTRL_ON | AMETA_CTRL_LEFT_ON | AMETA_CTRL_RIGHT_ON + | AMETA_ALT_ON | AMETA_ALT_LEFT_ON | AMETA_ALT_RIGHT_ON + | AMETA_META_ON | AMETA_META_LEFT_ON | AMETA_META_RIGHT_ON; + int32_t unmatchedMetaState = eventMetaState & ~behaviorMetaState & EXACT_META_STATES; + if (behaviorMetaState & AMETA_CTRL_ON) { + unmatchedMetaState &= ~(AMETA_CTRL_LEFT_ON | AMETA_CTRL_RIGHT_ON); + } else if (behaviorMetaState & (AMETA_CTRL_LEFT_ON | AMETA_CTRL_RIGHT_ON)) { + unmatchedMetaState &= ~AMETA_CTRL_ON; + } + if (behaviorMetaState & AMETA_ALT_ON) { + unmatchedMetaState &= ~(AMETA_ALT_LEFT_ON | AMETA_ALT_RIGHT_ON); + } else if (behaviorMetaState & (AMETA_ALT_LEFT_ON | AMETA_ALT_RIGHT_ON)) { + unmatchedMetaState &= ~AMETA_ALT_ON; + } + if (behaviorMetaState & AMETA_META_ON) { + unmatchedMetaState &= ~(AMETA_META_LEFT_ON | AMETA_META_RIGHT_ON); + } else if (behaviorMetaState & (AMETA_META_LEFT_ON | AMETA_META_RIGHT_ON)) { + unmatchedMetaState &= ~AMETA_META_ON; + } + return !unmatchedMetaState; + } + return false; +} + bool KeyCharacterMap::findKey(char16_t ch, int32_t* outKeyCode, int32_t* outMetaState) const { if (!ch) { return false; @@ -419,6 +555,79 @@ void KeyCharacterMap::addLockedMetaKey(Vector& outEvents, } } +#if HAVE_ANDROID_OS +sp KeyCharacterMap::readFromParcel(Parcel* parcel) { + sp map = new KeyCharacterMap(); + map->mType = parcel->readInt32(); + size_t numKeys = parcel->readInt32(); + if (parcel->errorCheck()) { + return NULL; + } + + for (size_t i = 0; i < numKeys; i++) { + int32_t keyCode = parcel->readInt32(); + char16_t label = parcel->readInt32(); + char16_t number = parcel->readInt32(); + if (parcel->errorCheck()) { + return NULL; + } + + Key* key = new Key(); + key->label = label; + key->number = number; + map->mKeys.add(keyCode, key); + + Behavior* lastBehavior = NULL; + while (parcel->readInt32()) { + int32_t metaState = parcel->readInt32(); + char16_t character = parcel->readInt32(); + int32_t fallbackKeyCode = parcel->readInt32(); + if (parcel->errorCheck()) { + return NULL; + } + + Behavior* behavior = new Behavior(); + behavior->metaState = metaState; + behavior->character = character; + behavior->fallbackKeyCode = fallbackKeyCode; + if (lastBehavior) { + lastBehavior->next = behavior; + } else { + key->firstBehavior = behavior; + } + lastBehavior = behavior; + } + + if (parcel->errorCheck()) { + return NULL; + } + } + return map; +} + +void KeyCharacterMap::writeToParcel(Parcel* parcel) const { + parcel->writeInt32(mType); + + size_t numKeys = mKeys.size(); + parcel->writeInt32(numKeys); + for (size_t i = 0; i < numKeys; i++) { + int32_t keyCode = mKeys.keyAt(i); + const Key* key = mKeys.valueAt(i); + parcel->writeInt32(keyCode); + parcel->writeInt32(key->label); + parcel->writeInt32(key->number); + for (const Behavior* behavior = key->firstBehavior; behavior != NULL; + behavior = behavior->next) { + parcel->writeInt32(1); + parcel->writeInt32(behavior->metaState); + parcel->writeInt32(behavior->character); + parcel->writeInt32(behavior->fallbackKeyCode); + } + parcel->writeInt32(0); + } +} +#endif + // --- KeyCharacterMap::Key --- @@ -426,6 +635,11 @@ KeyCharacterMap::Key::Key() : label(0), number(0), firstBehavior(NULL) { } +KeyCharacterMap::Key::Key(const Key& other) : + label(other.label), number(other.number), + firstBehavior(other.firstBehavior ? new Behavior(*other.firstBehavior) : NULL) { +} + KeyCharacterMap::Key::~Key() { Behavior* behavior = firstBehavior; while (behavior) { @@ -442,11 +656,17 @@ KeyCharacterMap::Behavior::Behavior() : next(NULL), metaState(0), character(0), fallbackKeyCode(0) { } +KeyCharacterMap::Behavior::Behavior(const Behavior& other) : + next(other.next ? new Behavior(*other.next) : NULL), + metaState(other.metaState), character(other.character), + fallbackKeyCode(other.fallbackKeyCode) { +} + // --- KeyCharacterMap::Parser --- -KeyCharacterMap::Parser::Parser(KeyCharacterMap* map, Tokenizer* tokenizer) : - mMap(map), mTokenizer(tokenizer), mState(STATE_TOP) { +KeyCharacterMap::Parser::Parser(KeyCharacterMap* map, Tokenizer* tokenizer, Format format) : + mMap(map), mTokenizer(tokenizer), mFormat(format), mState(STATE_TOP) { } KeyCharacterMap::Parser::~Parser() { @@ -469,6 +689,10 @@ status_t KeyCharacterMap::Parser::parse() { mTokenizer->skipDelimiters(WHITESPACE); status_t status = parseType(); if (status) return status; + } else if (keywordToken == "map") { + mTokenizer->skipDelimiters(WHITESPACE); + status_t status = parseMap(); + if (status) return status; } else if (keywordToken == "key") { mTokenizer->skipDelimiters(WHITESPACE); status_t status = parseKey(); @@ -489,8 +713,8 @@ status_t KeyCharacterMap::Parser::parse() { } mTokenizer->skipDelimiters(WHITESPACE); - if (!mTokenizer->isEol()) { - ALOGE("%s: Expected end of line, got '%s'.", + if (!mTokenizer->isEol() && mTokenizer->peekChar() != '#') { + ALOGE("%s: Expected end of line or trailing comment, got '%s'.", mTokenizer->getLocation().string(), mTokenizer->peekRemainderOfLine().string()); return BAD_VALUE; @@ -507,11 +731,26 @@ status_t KeyCharacterMap::Parser::parse() { } if (mMap->mType == KEYBOARD_TYPE_UNKNOWN) { - ALOGE("%s: Missing required keyboard 'type' declaration.", + ALOGE("%s: Keyboard layout missing required keyboard 'type' declaration.", mTokenizer->getLocation().string()); return BAD_VALUE; } + if (mFormat == FORMAT_BASE) { + if (mMap->mType == KEYBOARD_TYPE_OVERLAY) { + ALOGE("%s: Base keyboard layout must specify a keyboard 'type' other than 'OVERLAY'.", + mTokenizer->getLocation().string()); + return BAD_VALUE; + } + } else if (mFormat == FORMAT_OVERLAY) { + if (mMap->mType != KEYBOARD_TYPE_OVERLAY) { + ALOGE("%s: Overlay keyboard layout missing required keyboard " + "'type OVERLAY' declaration.", + mTokenizer->getLocation().string()); + return BAD_VALUE; + } + } + return NO_ERROR; } @@ -534,6 +773,8 @@ status_t KeyCharacterMap::Parser::parseType() { type = KEYBOARD_TYPE_FULL; } else if (typeToken == "SPECIAL_FUNCTION") { type = KEYBOARD_TYPE_SPECIAL_FUNCTION; + } else if (typeToken == "OVERLAY") { + type = KEYBOARD_TYPE_OVERLAY; } else { ALOGE("%s: Expected keyboard type label, got '%s'.", mTokenizer->getLocation().string(), typeToken.string()); @@ -547,6 +788,58 @@ status_t KeyCharacterMap::Parser::parseType() { return NO_ERROR; } +status_t KeyCharacterMap::Parser::parseMap() { + String8 keywordToken = mTokenizer->nextToken(WHITESPACE); + if (keywordToken == "key") { + mTokenizer->skipDelimiters(WHITESPACE); + return parseMapKey(); + } + ALOGE("%s: Expected keyword after 'map', got '%s'.", mTokenizer->getLocation().string(), + keywordToken.string()); + return BAD_VALUE; +} + +status_t KeyCharacterMap::Parser::parseMapKey() { + String8 codeToken = mTokenizer->nextToken(WHITESPACE); + bool mapUsage = false; + if (codeToken == "usage") { + mapUsage = true; + mTokenizer->skipDelimiters(WHITESPACE); + codeToken = mTokenizer->nextToken(WHITESPACE); + } + + char* end; + int32_t code = int32_t(strtol(codeToken.string(), &end, 0)); + if (*end) { + ALOGE("%s: Expected key %s number, got '%s'.", mTokenizer->getLocation().string(), + mapUsage ? "usage" : "scan code", codeToken.string()); + return BAD_VALUE; + } + KeyedVector& map = + mapUsage ? mMap->mKeysByUsageCode : mMap->mKeysByScanCode; + if (map.indexOfKey(code) >= 0) { + ALOGE("%s: Duplicate entry for key %s '%s'.", mTokenizer->getLocation().string(), + mapUsage ? "usage" : "scan code", codeToken.string()); + return BAD_VALUE; + } + + mTokenizer->skipDelimiters(WHITESPACE); + String8 keyCodeToken = mTokenizer->nextToken(WHITESPACE); + int32_t keyCode = getKeyCodeByLabel(keyCodeToken.string()); + if (!keyCode) { + ALOGE("%s: Expected key code label, got '%s'.", mTokenizer->getLocation().string(), + keyCodeToken.string()); + return BAD_VALUE; + } + +#if DEBUG_PARSER + ALOGD("Parsed map key %s: code=%d, keyCode=%d.", + mapUsage ? "usage" : "scan code", code, keyCode); +#endif + map.add(code, keyCode); + return NO_ERROR; +} + status_t KeyCharacterMap::Parser::parseKey() { String8 keyCodeToken = mTokenizer->nextToken(WHITESPACE); int32_t keyCode = getKeyCodeByLabel(keyCodeToken.string()); @@ -579,10 +872,11 @@ status_t KeyCharacterMap::Parser::parseKey() { } status_t KeyCharacterMap::Parser::parseKeyProperty() { + Key* key = mMap->mKeys.valueFor(mKeyCode); String8 token = mTokenizer->nextToken(WHITESPACE_OR_PROPERTY_DELIMITER); if (token == "}") { mState = STATE_TOP; - return NO_ERROR; + return finishKey(key); } Vector properties; @@ -679,10 +973,9 @@ status_t KeyCharacterMap::Parser::parseKeyProperty() { } mTokenizer->skipDelimiters(WHITESPACE); - } while (!mTokenizer->isEol()); + } while (!mTokenizer->isEol() && mTokenizer->peekChar() != '#'); // Add the behavior. - Key* key = mMap->mKeys.valueFor(mKeyCode); for (size_t i = 0; i < properties.size(); i++) { const Property& property = properties.itemAt(i); switch (property.property) { @@ -731,6 +1024,28 @@ status_t KeyCharacterMap::Parser::parseKeyProperty() { return NO_ERROR; } +status_t KeyCharacterMap::Parser::finishKey(Key* key) { + // Fill in default number property. + if (!key->number) { + char16_t digit = 0; + char16_t symbol = 0; + for (Behavior* b = key->firstBehavior; b; b = b->next) { + char16_t ch = b->character; + if (ch) { + if (ch >= '0' && ch <= '9') { + digit = ch; + } else if (ch == '(' || ch == ')' || ch == '#' || ch == '*' + || ch == '-' || ch == '+' || ch == ',' || ch == '.' + || ch == '\'' || ch == ':' || ch == ';' || ch == '/') { + symbol = ch; + } + } + } + key->number = digit ? digit : symbol; + } + return NO_ERROR; +} + status_t KeyCharacterMap::Parser::parseModifier(const String8& token, int32_t* outMetaState) { if (token == "base") { *outMetaState = 0; diff --git a/widget/gonk/libui/KeyCharacterMap.h b/widget/gonk/libui/KeyCharacterMap.h index 3efe1cdf40ed..c7a6841059d2 100644 --- a/widget/gonk/libui/KeyCharacterMap.h +++ b/widget/gonk/libui/KeyCharacterMap.h @@ -14,17 +14,22 @@ * limitations under the License. */ -#ifndef _UI_KEY_CHARACTER_MAP_H -#define _UI_KEY_CHARACTER_MAP_H +#ifndef _ANDROIDFW_KEY_CHARACTER_MAP_H +#define _ANDROIDFW_KEY_CHARACTER_MAP_H #include +#if HAVE_ANDROID_OS +#include +#endif + #include "Input.h" #include #include #include "Tokenizer.h" -#include "String8.h" -#include "Unicode.h" +#include +#include +#include namespace android { @@ -32,8 +37,10 @@ namespace android { * Describes a mapping from Android key codes to characters. * Also specifies other functions of the keyboard such as the keyboard type * and key modifier semantics. + * + * This object is immutable after it has been loaded. */ -class KeyCharacterMap { +class KeyCharacterMap : public RefBase { public: enum KeyboardType { KEYBOARD_TYPE_UNKNOWN = 0, @@ -42,6 +49,17 @@ public: KEYBOARD_TYPE_ALPHA = 3, KEYBOARD_TYPE_FULL = 4, KEYBOARD_TYPE_SPECIAL_FUNCTION = 5, + KEYBOARD_TYPE_OVERLAY = 6, + }; + + enum Format { + // Base keyboard layout, may contain device-specific options, such as "type" declaration. + FORMAT_BASE = 0, + // Overlay keyboard layout, more restrictive, may be published by applications, + // cannot override device-specific options. + FORMAT_OVERLAY = 1, + // Either base or overlay layout ok. + FORMAT_ANY = 2, }; // Substitute key code and meta state for fallback action. @@ -50,9 +68,19 @@ public: int32_t metaState; }; - ~KeyCharacterMap(); + /* Loads a key character map from a file. */ + static status_t load(const String8& filename, Format format, sp* outMap); - static status_t load(const String8& filename, KeyCharacterMap** outMap); + /* Loads a key character map from its string contents. */ + static status_t loadContents(const String8& filename, + const char* contents, Format format, sp* outMap); + + /* Combines a base key character map and an overlay. */ + static sp combine(const sp& base, + const sp& overlay); + + /* Returns an empty key character map. */ + static sp empty(); /* Gets the keyboard type. */ int32_t getKeyboardType() const; @@ -92,9 +120,25 @@ public: bool getEvents(int32_t deviceId, const char16_t* chars, size_t numChars, Vector& outEvents) const; + /* Maps a scan code and usage code to a key code, in case this key map overrides + * the mapping in some way. */ + status_t mapKey(int32_t scanCode, int32_t usageCode, int32_t* outKeyCode) const; + +#if HAVE_ANDROID_OS + /* Reads a key map from a parcel. */ + static sp readFromParcel(Parcel* parcel); + + /* Writes a key map to a parcel. */ + void writeToParcel(Parcel* parcel) const; +#endif + +protected: + virtual ~KeyCharacterMap(); + private: struct Behavior { Behavior(); + Behavior(const Behavior& other); /* The next behavior in the list, or NULL if none. */ Behavior* next; @@ -111,6 +155,7 @@ private: struct Key { Key(); + Key(const Key& other); ~Key(); /* The single character label printed on the key, or 0 if none. */ @@ -146,33 +191,46 @@ private: KeyCharacterMap* mMap; Tokenizer* mTokenizer; + Format mFormat; State mState; int32_t mKeyCode; public: - Parser(KeyCharacterMap* map, Tokenizer* tokenizer); + Parser(KeyCharacterMap* map, Tokenizer* tokenizer, Format format); ~Parser(); status_t parse(); private: status_t parseType(); + status_t parseMap(); + status_t parseMapKey(); status_t parseKey(); status_t parseKeyProperty(); + status_t finishKey(Key* key); status_t parseModifier(const String8& token, int32_t* outMetaState); status_t parseCharacterLiteral(char16_t* outCharacter); }; + static sp sEmpty; + KeyedVector mKeys; int mType; + KeyedVector mKeysByScanCode; + KeyedVector mKeysByUsageCode; + KeyCharacterMap(); + KeyCharacterMap(const KeyCharacterMap& other); bool getKey(int32_t keyCode, const Key** outKey) const; bool getKeyBehavior(int32_t keyCode, int32_t metaState, const Key** outKey, const Behavior** outBehavior) const; + static bool matchesMetaState(int32_t eventMetaState, int32_t behaviorMetaState); bool findKey(char16_t ch, int32_t* outKeyCode, int32_t* outMetaState) const; + static status_t load(Tokenizer* tokenizer, Format format, sp* outMap); + static void addKey(Vector& outEvents, int32_t deviceId, int32_t keyCode, int32_t metaState, bool down, nsecs_t time); static void addMetaKeys(Vector& outEvents, @@ -196,4 +254,4 @@ private: } // namespace android -#endif // _UI_KEY_CHARACTER_MAP_H +#endif // _ANDROIDFW_KEY_CHARACTER_MAP_H diff --git a/widget/gonk/libui/KeyLayoutMap.cpp b/widget/gonk/libui/KeyLayoutMap.cpp index 2c9cb24e2cff..cd8fef519b65 100644 --- a/widget/gonk/libui/KeyLayoutMap.cpp +++ b/widget/gonk/libui/KeyLayoutMap.cpp @@ -15,15 +15,15 @@ */ #define LOG_TAG "KeyLayoutMap" +#include "cutils_log.h" #include -#include "utils_Log.h" #include "android_keycodes.h" #include "Keyboard.h" #include "KeyLayoutMap.h" #include #include "Tokenizer.h" -#include "Timers.h" +#include // Enables debug output for the parser. #define DEBUG_PARSER 0 @@ -47,23 +47,23 @@ KeyLayoutMap::KeyLayoutMap() { KeyLayoutMap::~KeyLayoutMap() { } -status_t KeyLayoutMap::load(const String8& filename, KeyLayoutMap** outMap) { - *outMap = NULL; +status_t KeyLayoutMap::load(const String8& filename, sp* outMap) { + outMap->clear(); Tokenizer* tokenizer; status_t status = Tokenizer::open(filename, &tokenizer); if (status) { ALOGE("Error %d opening key layout map file %s.", status, filename.string()); } else { - KeyLayoutMap* map = new KeyLayoutMap(); - if (!map) { + sp map = new KeyLayoutMap(); + if (!map.get()) { ALOGE("Error allocating key layout map."); status = NO_MEMORY; } else { #if DEBUG_PARSER_PERFORMANCE nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC); #endif - Parser parser(map, tokenizer); + Parser parser(map.get(), tokenizer); status = parser.parse(); #if DEBUG_PARSER_PERFORMANCE nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime; @@ -71,9 +71,7 @@ status_t KeyLayoutMap::load(const String8& filename, KeyLayoutMap** outMap) { tokenizer->getFilename().string(), tokenizer->getLineNumber(), elapsedTime / 1000000.0); #endif - if (status) { - delete map; - } else { + if (!status) { *outMap = map; } } @@ -82,32 +80,49 @@ status_t KeyLayoutMap::load(const String8& filename, KeyLayoutMap** outMap) { return status; } -status_t KeyLayoutMap::mapKey(int32_t scanCode, int32_t* keyCode, uint32_t* flags) const { - ssize_t index = mKeys.indexOfKey(scanCode); - if (index < 0) { +status_t KeyLayoutMap::mapKey(int32_t scanCode, int32_t usageCode, + int32_t* outKeyCode, uint32_t* outFlags) const { + const Key* key = getKey(scanCode, usageCode); + if (!key) { #if DEBUG_MAPPING - ALOGD("mapKey: scanCode=%d ~ Failed.", scanCode); + ALOGD("mapKey: scanCode=%d, usageCode=0x%08x ~ Failed.", scanCode, usageCode); #endif - *keyCode = AKEYCODE_UNKNOWN; - *flags = 0; + *outKeyCode = AKEYCODE_UNKNOWN; + *outFlags = 0; return NAME_NOT_FOUND; } - const Key& k = mKeys.valueAt(index); - *keyCode = k.keyCode; - *flags = k.flags; + *outKeyCode = key->keyCode; + *outFlags = key->flags; #if DEBUG_MAPPING - ALOGD("mapKey: scanCode=%d ~ Result keyCode=%d, flags=0x%08x.", scanCode, *keyCode, *flags); + ALOGD("mapKey: scanCode=%d, usageCode=0x%08x ~ Result keyCode=%d, outFlags=0x%08x.", + scanCode, usageCode, *outKeyCode, *outFlags); #endif return NO_ERROR; } +const KeyLayoutMap::Key* KeyLayoutMap::getKey(int32_t scanCode, int32_t usageCode) const { + if (usageCode) { + ssize_t index = mKeysByUsageCode.indexOfKey(usageCode); + if (index >= 0) { + return &mKeysByUsageCode.valueAt(index); + } + } + if (scanCode) { + ssize_t index = mKeysByScanCode.indexOfKey(scanCode); + if (index >= 0) { + return &mKeysByScanCode.valueAt(index); + } + } + return NULL; +} + status_t KeyLayoutMap::findScanCodesForKey(int32_t keyCode, Vector* outScanCodes) const { - const size_t N = mKeys.size(); + const size_t N = mKeysByScanCode.size(); for (size_t i=0; iadd(mKeys.keyAt(i)); + if (mKeysByScanCode.valueAt(i).keyCode == keyCode) { + outScanCodes->add(mKeysByScanCode.keyAt(i)); } } return NO_ERROR; @@ -170,8 +185,8 @@ status_t KeyLayoutMap::Parser::parse() { } mTokenizer->skipDelimiters(WHITESPACE); - if (!mTokenizer->isEol()) { - ALOGE("%s: Expected end of line, got '%s'.", + if (!mTokenizer->isEol() && mTokenizer->peekChar() != '#') { + ALOGE("%s: Expected end of line or trailing comment, got '%s'.", mTokenizer->getLocation().string(), mTokenizer->peekRemainderOfLine().string()); return BAD_VALUE; @@ -184,17 +199,26 @@ status_t KeyLayoutMap::Parser::parse() { } status_t KeyLayoutMap::Parser::parseKey() { - String8 scanCodeToken = mTokenizer->nextToken(WHITESPACE); + String8 codeToken = mTokenizer->nextToken(WHITESPACE); + bool mapUsage = false; + if (codeToken == "usage") { + mapUsage = true; + mTokenizer->skipDelimiters(WHITESPACE); + codeToken = mTokenizer->nextToken(WHITESPACE); + } + char* end; - int32_t scanCode = int32_t(strtol(scanCodeToken.string(), &end, 0)); + int32_t code = int32_t(strtol(codeToken.string(), &end, 0)); if (*end) { - ALOGE("%s: Expected key scan code number, got '%s'.", mTokenizer->getLocation().string(), - scanCodeToken.string()); + ALOGE("%s: Expected key %s number, got '%s'.", mTokenizer->getLocation().string(), + mapUsage ? "usage" : "scan code", codeToken.string()); return BAD_VALUE; } - if (mMap->mKeys.indexOfKey(scanCode) >= 0) { - ALOGE("%s: Duplicate entry for key scan code '%s'.", mTokenizer->getLocation().string(), - scanCodeToken.string()); + KeyedVector& map = + mapUsage ? mMap->mKeysByUsageCode : mMap->mKeysByScanCode; + if (map.indexOfKey(code) >= 0) { + ALOGE("%s: Duplicate entry for key %s '%s'.", mTokenizer->getLocation().string(), + mapUsage ? "usage" : "scan code", codeToken.string()); return BAD_VALUE; } @@ -210,7 +234,7 @@ status_t KeyLayoutMap::Parser::parseKey() { uint32_t flags = 0; for (;;) { mTokenizer->skipDelimiters(WHITESPACE); - if (mTokenizer->isEol()) break; + if (mTokenizer->isEol() || mTokenizer->peekChar() == '#') break; String8 flagToken = mTokenizer->nextToken(WHITESPACE); uint32_t flag = getKeyFlagByLabel(flagToken.string()); @@ -228,12 +252,13 @@ status_t KeyLayoutMap::Parser::parseKey() { } #if DEBUG_PARSER - ALOGD("Parsed key: scanCode=%d, keyCode=%d, flags=0x%08x.", scanCode, keyCode, flags); + ALOGD("Parsed key %s: code=%d, keyCode=%d, flags=0x%08x.", + mapUsage ? "usage" : "scan code", code, keyCode, flags); #endif Key key; key.keyCode = keyCode; key.flags = flags; - mMap->mKeys.add(scanCode, key); + map.add(code, key); return NO_ERROR; } @@ -307,7 +332,7 @@ status_t KeyLayoutMap::Parser::parseAxis() { for (;;) { mTokenizer->skipDelimiters(WHITESPACE); - if (mTokenizer->isEol()) { + if (mTokenizer->isEol() || mTokenizer->peekChar() == '#') { break; } String8 keywordToken = mTokenizer->nextToken(WHITESPACE); diff --git a/widget/gonk/libui/KeyLayoutMap.h b/widget/gonk/libui/KeyLayoutMap.h index 9b83114ea364..c6f6cdcc518f 100644 --- a/widget/gonk/libui/KeyLayoutMap.h +++ b/widget/gonk/libui/KeyLayoutMap.h @@ -14,13 +14,14 @@ * limitations under the License. */ -#ifndef _UI_KEY_LAYOUT_MAP_H -#define _UI_KEY_LAYOUT_MAP_H +#ifndef _ANDROIDFW_KEY_LAYOUT_MAP_H +#define _ANDROIDFW_KEY_LAYOUT_MAP_H #include #include #include #include "Tokenizer.h" +#include namespace android { @@ -56,29 +57,36 @@ struct AxisInfo { /** * Describes a mapping from keyboard scan codes and joystick axes to Android key codes and axes. + * + * This object is immutable after it has been loaded. */ -class KeyLayoutMap { +class KeyLayoutMap : public RefBase { public: - ~KeyLayoutMap(); + static status_t load(const String8& filename, sp* outMap); - static status_t load(const String8& filename, KeyLayoutMap** outMap); - - status_t mapKey(int32_t scanCode, int32_t* keyCode, uint32_t* flags) const; + status_t mapKey(int32_t scanCode, int32_t usageCode, + int32_t* outKeyCode, uint32_t* outFlags) const; status_t findScanCodesForKey(int32_t keyCode, Vector* outScanCodes) const; status_t mapAxis(int32_t scanCode, AxisInfo* outAxisInfo) const; +protected: + virtual ~KeyLayoutMap(); + private: struct Key { int32_t keyCode; uint32_t flags; }; - KeyedVector mKeys; + KeyedVector mKeysByScanCode; + KeyedVector mKeysByUsageCode; KeyedVector mAxes; KeyLayoutMap(); + const Key* getKey(int32_t scanCode, int32_t usageCode) const; + class Parser { KeyLayoutMap* mMap; Tokenizer* mTokenizer; @@ -96,4 +104,4 @@ private: } // namespace android -#endif // _UI_KEY_LAYOUT_MAP_H +#endif // _ANDROIDFW_KEY_LAYOUT_MAP_H diff --git a/widget/gonk/libui/Keyboard.cpp b/widget/gonk/libui/Keyboard.cpp index d10086f9aa31..09143595da2f 100644 --- a/widget/gonk/libui/Keyboard.cpp +++ b/widget/gonk/libui/Keyboard.cpp @@ -15,16 +15,17 @@ */ #define LOG_TAG "Keyboard" +#include "cutils_log.h" #include #include #include -#include "utils_Log.h" #include "Keyboard.h" #include "KeycodeLabels.h" #include "KeyLayoutMap.h" #include "KeyCharacterMap.h" +#include "InputDevice.h" #include #include @@ -32,13 +33,10 @@ namespace android { // --- KeyMap --- -KeyMap::KeyMap() : - keyLayoutMap(NULL), keyCharacterMap(NULL) { +KeyMap::KeyMap() { } KeyMap::~KeyMap() { - delete keyLayoutMap; - delete keyCharacterMap; } status_t KeyMap::load(const InputDeviceIdentifier& deviceIdenfifier, @@ -114,14 +112,12 @@ status_t KeyMap::loadKeyLayout(const InputDeviceIdentifier& deviceIdentifier, return NAME_NOT_FOUND; } - KeyLayoutMap* map; - status_t status = KeyLayoutMap::load(path, &map); + status_t status = KeyLayoutMap::load(path, &keyLayoutMap); if (status) { return status; } keyLayoutFile.setTo(path); - keyLayoutMap = map; return OK; } @@ -133,14 +129,13 @@ status_t KeyMap::loadKeyCharacterMap(const InputDeviceIdentifier& deviceIdentifi return NAME_NOT_FOUND; } - KeyCharacterMap* map; - status_t status = KeyCharacterMap::load(path, &map); + status_t status = KeyCharacterMap::load(path, + KeyCharacterMap::FORMAT_BASE, &keyCharacterMap); if (status) { return status; } keyCharacterMapFile.setTo(path); - keyCharacterMap = map; return OK; } diff --git a/widget/gonk/libui/Keyboard.h b/widget/gonk/libui/Keyboard.h index 8e9e10abc8b3..1dacbb3a2a6b 100644 --- a/widget/gonk/libui/Keyboard.h +++ b/widget/gonk/libui/Keyboard.h @@ -14,13 +14,14 @@ * limitations under the License. */ -#ifndef _UI_KEYBOARD_H -#define _UI_KEYBOARD_H +#ifndef _ANDROIDFW_KEYBOARD_H +#define _ANDROIDFW_KEYBOARD_H #include "Input.h" +#include "InputDevice.h" #include -#include "String8.h" -#include "PropertyMap.h" +#include +#include namespace android { @@ -42,10 +43,10 @@ class KeyCharacterMap; class KeyMap { public: String8 keyLayoutFile; - KeyLayoutMap* keyLayoutMap; + sp keyLayoutMap; String8 keyCharacterMapFile; - KeyCharacterMap* keyCharacterMap; + sp keyCharacterMap; KeyMap(); ~KeyMap(); @@ -116,4 +117,4 @@ extern bool isMetaKey(int32_t keyCode); } // namespace android -#endif // _UI_KEYBOARD_H +#endif // _ANDROIDFW_KEYBOARD_H diff --git a/widget/gonk/libui/KeycodeLabels.h b/widget/gonk/libui/KeycodeLabels.h index cb4a17752530..bcc2937c2fb7 100644 --- a/widget/gonk/libui/KeycodeLabels.h +++ b/widget/gonk/libui/KeycodeLabels.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef _UI_KEYCODE_LABELS_H -#define _UI_KEYCODE_LABELS_H +#ifndef _ANDROIDFW_KEYCODE_LABELS_H +#define _ANDROIDFW_KEYCODE_LABELS_H #include "android_keycodes.h" @@ -235,6 +235,17 @@ static const KeycodeLabel KEYCODES[] = { { "CALENDAR", 208 }, { "MUSIC", 209 }, { "CALCULATOR", 210 }, + { "ZENKAKU_HANKAKU", 211 }, + { "EISU", 212 }, + { "MUHENKAN", 213 }, + { "HENKAN", 214 }, + { "KATAKANA_HIRAGANA", 215 }, + { "YEN", 216 }, + { "RO", 217 }, + { "KANA", 218 }, + { "ASSIST", 219 }, + { "BRIGHTNESS_DOWN", 220 }, + { "BRIGHTNESS_UP", 221 }, // NOTE: If you add a new keycode here you must also add it to several other files. // Refer to frameworks/base/core/java/android/view/KeyEvent.java for the full list. @@ -307,4 +318,4 @@ static const KeycodeLabel AXES[] = { { NULL, -1 } }; -#endif // _UI_KEYCODE_LABELS_H +#endif // _ANDROIDFW_KEYCODE_LABELS_H diff --git a/widget/gonk/libui/PixelFormat.cpp b/widget/gonk/libui/PixelFormat.cpp deleted file mode 100644 index ba86bf3d8647..000000000000 --- a/widget/gonk/libui/PixelFormat.cpp +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "PixelFormat.h" -#include -#include - -namespace android { - -static const int COMPONENT_YUV = 0xFF; - -size_t PixelFormatInfo::getScanlineSize(unsigned int width) const -{ - size_t size; - if (components == COMPONENT_YUV) { - // YCbCr formats are different. - size = (width * bitsPerPixel)>>3; - } else { - size = width * bytesPerPixel; - } - return size; -} - -#ifdef HAVE_ANDROID_OS -ssize_t bytesPerPixel(PixelFormat format) -{ - PixelFormatInfo info; - status_t err = getPixelFormatInfo(format, &info); - return (err < 0) ? err : info.bytesPerPixel; -} - -ssize_t bitsPerPixel(PixelFormat format) -{ - PixelFormatInfo info; - status_t err = getPixelFormatInfo(format, &info); - return (err < 0) ? err : info.bitsPerPixel; -} - -status_t getPixelFormatInfo(PixelFormat format, PixelFormatInfo* info) -{ - if (format < 0) - return BAD_VALUE; - - if (info->version != sizeof(PixelFormatInfo)) - return INVALID_OPERATION; - - // YUV format from the HAL are handled here - switch (format) { - case HAL_PIXEL_FORMAT_YCbCr_422_SP: - case HAL_PIXEL_FORMAT_YCbCr_422_I: - info->bitsPerPixel = 16; - goto done; - case HAL_PIXEL_FORMAT_YCrCb_420_SP: - case HAL_PIXEL_FORMAT_YV12: - info->bitsPerPixel = 12; - done: - info->format = format; - info->components = COMPONENT_YUV; - info->bytesPerPixel = 1; - info->h_alpha = 0; - info->l_alpha = 0; - info->h_red = info->h_green = info->h_blue = 8; - info->l_red = info->l_green = info->l_blue = 0; - return NO_ERROR; - } - - size_t numEntries; - const GGLFormat *i = gglGetPixelFormatTable(&numEntries) + format; - bool valid = uint32_t(format) < numEntries; - if (!valid) { - return BAD_INDEX; - } - - #define COMPONENT(name) \ - case GGL_##name: info->components = PixelFormatInfo::name; break; - - switch (i->components) { - COMPONENT(ALPHA) - COMPONENT(RGB) - COMPONENT(RGBA) - COMPONENT(LUMINANCE) - COMPONENT(LUMINANCE_ALPHA) - default: - return BAD_INDEX; - } - - #undef COMPONENT - - info->format = format; - info->bytesPerPixel = i->size; - info->bitsPerPixel = i->bitsPerPixel; - info->h_alpha = i->ah; - info->l_alpha = i->al; - info->h_red = i->rh; - info->l_red = i->rl; - info->h_green = i->gh; - info->l_green = i->gl; - info->h_blue = i->bh; - info->l_blue = i->bl; - - return NO_ERROR; -} -#endif // HAVE_ANDROID_OS - -}; // namespace android - diff --git a/widget/gonk/libui/PixelFormat.h b/widget/gonk/libui/PixelFormat.h deleted file mode 100644 index 2e67754d50e0..000000000000 --- a/widget/gonk/libui/PixelFormat.h +++ /dev/null @@ -1,139 +0,0 @@ -/* - * Copyright (C) 2005 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// - -// Pixel formats used across the system. -// These formats might not all be supported by all renderers, for instance -// skia or SurfaceFlinger are not required to support all of these formats -// (either as source or destination) - -// XXX: we should consolidate these formats and skia's - -#ifndef UI_PIXELFORMAT_H -#define UI_PIXELFORMAT_H - -#include -#include -#include -#include -#include - -namespace android { - -enum { - // - // these constants need to match those - // in graphics/PixelFormat.java & pixelflinger/format.h - // - PIXEL_FORMAT_UNKNOWN = 0, - PIXEL_FORMAT_NONE = 0, - - // logical pixel formats used by the SurfaceFlinger ----------------------- - PIXEL_FORMAT_CUSTOM = -4, - // Custom pixel-format described by a PixelFormatInfo structure - - PIXEL_FORMAT_TRANSLUCENT = -3, - // System chooses a format that supports translucency (many alpha bits) - - PIXEL_FORMAT_TRANSPARENT = -2, - // System chooses a format that supports transparency - // (at least 1 alpha bit) - - PIXEL_FORMAT_OPAQUE = -1, - // System chooses an opaque format (no alpha bits required) - - // real pixel formats supported for rendering ----------------------------- - - PIXEL_FORMAT_RGBA_8888 = HAL_PIXEL_FORMAT_RGBA_8888, // 4x8-bit RGBA - PIXEL_FORMAT_RGBX_8888 = HAL_PIXEL_FORMAT_RGBX_8888, // 4x8-bit RGB0 - PIXEL_FORMAT_RGB_888 = HAL_PIXEL_FORMAT_RGB_888, // 3x8-bit RGB - PIXEL_FORMAT_RGB_565 = HAL_PIXEL_FORMAT_RGB_565, // 16-bit RGB - PIXEL_FORMAT_BGRA_8888 = HAL_PIXEL_FORMAT_BGRA_8888, // 4x8-bit BGRA - PIXEL_FORMAT_RGBA_5551 = HAL_PIXEL_FORMAT_RGBA_5551, // 16-bit ARGB - PIXEL_FORMAT_RGBA_4444 = HAL_PIXEL_FORMAT_RGBA_4444, // 16-bit ARGB - PIXEL_FORMAT_A_8 = GGL_PIXEL_FORMAT_A_8, // 8-bit A - PIXEL_FORMAT_L_8 = GGL_PIXEL_FORMAT_L_8, // 8-bit L (R=G=B=L) - PIXEL_FORMAT_LA_88 = GGL_PIXEL_FORMAT_LA_88, // 16-bit LA - PIXEL_FORMAT_RGB_332 = GGL_PIXEL_FORMAT_RGB_332, // 8-bit RGB - - // New formats can be added if they're also defined in - // pixelflinger/format.h -}; - -typedef int32_t PixelFormat; - -struct PixelFormatInfo -{ - enum { - INDEX_ALPHA = 0, - INDEX_RED = 1, - INDEX_GREEN = 2, - INDEX_BLUE = 3 - }; - - enum { // components - ALPHA = 1, - RGB = 2, - RGBA = 3, - LUMINANCE = 4, - LUMINANCE_ALPHA = 5, - OTHER = 0xFF - }; - - struct szinfo { - uint8_t h; - uint8_t l; - }; - - inline PixelFormatInfo() : version(sizeof(PixelFormatInfo)) { } - size_t getScanlineSize(unsigned int width) const; - size_t getSize(size_t ci) const { - return (ci <= 3) ? (cinfo[ci].h - cinfo[ci].l) : 0; - } - size_t version; - PixelFormat format; - size_t bytesPerPixel; - size_t bitsPerPixel; - union { - szinfo cinfo[4]; - struct { - uint8_t h_alpha; - uint8_t l_alpha; - uint8_t h_red; - uint8_t l_red; - uint8_t h_green; - uint8_t l_green; - uint8_t h_blue; - uint8_t l_blue; - }; - }; - uint8_t components; - uint8_t reserved0[3]; - uint32_t reserved1; -}; - -#ifdef HAVE_ANDROID_OS -// Consider caching the results of these functions are they're not -// guaranteed to be fast. -ssize_t bytesPerPixel(PixelFormat format); -ssize_t bitsPerPixel(PixelFormat format); -status_t getPixelFormatInfo(PixelFormat format, PixelFormatInfo* info); -#endif - -}; // namespace android - -#endif // UI_PIXELFORMAT_H diff --git a/widget/gonk/libui/PointerController.cpp b/widget/gonk/libui/PointerController.cpp index dccc8316a610..ff80a0a9fec2 100644 --- a/widget/gonk/libui/PointerController.cpp +++ b/widget/gonk/libui/PointerController.cpp @@ -23,7 +23,7 @@ #include "PointerController.h" -#include +#include "cutils_log.h" #include #include @@ -54,9 +54,7 @@ static const nsecs_t POINTER_FADE_DURATION = 500 * 1000000LL; // 500 ms PointerController::PointerController(const sp& policy, const sp& looper, const sp& spriteController) : mPolicy(policy), mLooper(looper), mSpriteController(spriteController) { -#ifdef HAVE_ANDROID_OS mHandler = new WeakMessageHandler(this); -#endif AutoMutex _l(mLock); @@ -84,9 +82,7 @@ PointerController::PointerController(const sp& } PointerController::~PointerController() { -#ifdef HAVE_ANDROID_OS mLooper->removeMessages(mHandler); -#endif AutoMutex _l(mLock); @@ -311,9 +307,17 @@ void PointerController::setInactivityTimeout(InactivityTimeout inactivityTimeout } } -void PointerController::setDisplaySize(int32_t width, int32_t height) { +void PointerController::setDisplayViewport(int32_t width, int32_t height, int32_t orientation) { AutoMutex _l(mLock); + // Adjust to use the display's unrotated coordinate frame. + if (orientation == DISPLAY_ORIENTATION_90 + || orientation == DISPLAY_ORIENTATION_270) { + int32_t temp = height; + height = width; + width = temp; + } + if (mLocked.displayWidth != width || mLocked.displayHeight != height) { mLocked.displayWidth = width; mLocked.displayHeight = height; @@ -328,12 +332,7 @@ void PointerController::setDisplaySize(int32_t width, int32_t height) { } fadeOutAndReleaseAllSpotsLocked(); - updatePointerLocked(); } -} - -void PointerController::setDisplayOrientation(int32_t orientation) { - AutoMutex _l(mLock); if (mLocked.displayOrientation != orientation) { // Apply offsets to convert from the pixel top-left corner position to the pixel center. @@ -384,9 +383,9 @@ void PointerController::setDisplayOrientation(int32_t orientation) { mLocked.pointerX = x - 0.5f; mLocked.pointerY = y - 0.5f; mLocked.displayOrientation = orientation; - - updatePointerLocked(); } + + updatePointerLocked(); } void PointerController::setPointerIcon(const SpriteIcon& icon) { @@ -398,7 +397,6 @@ void PointerController::setPointerIcon(const SpriteIcon& icon) { updatePointerLocked(); } -#ifdef HAVE_ANDROID_OS void PointerController::handleMessage(const Message& message) { switch (message.what) { case MSG_ANIMATE: @@ -409,7 +407,6 @@ void PointerController::handleMessage(const Message& message) { break; } } -#endif void PointerController::doAnimate() { AutoMutex _l(mLock); @@ -467,28 +464,20 @@ void PointerController::startAnimationLocked() { if (!mLocked.animationPending) { mLocked.animationPending = true; mLocked.animationTime = systemTime(SYSTEM_TIME_MONOTONIC); -#ifdef HAVE_ANDROID_OS mLooper->sendMessageDelayed(ANIMATION_FRAME_INTERVAL, mHandler, Message(MSG_ANIMATE)); -#endif } } void PointerController::resetInactivityTimeoutLocked() { -#ifdef HAVE_ANDROID_OS mLooper->removeMessages(mHandler, MSG_INACTIVITY_TIMEOUT); -#endif nsecs_t timeout = mLocked.inactivityTimeout == INACTIVITY_TIMEOUT_SHORT ? INACTIVITY_TIMEOUT_DELAY_TIME_SHORT : INACTIVITY_TIMEOUT_DELAY_TIME_NORMAL; -#ifdef HAVE_ANDROID_OS mLooper->sendMessageDelayed(timeout, mHandler, MSG_INACTIVITY_TIMEOUT); -#endif } void PointerController::removeInactivityTimeoutLocked() { -#ifdef HAVE_ANDROID_OS mLooper->removeMessages(mHandler, MSG_INACTIVITY_TIMEOUT); -#endif } void PointerController::updatePointerLocked() { diff --git a/widget/gonk/libui/PointerController.h b/widget/gonk/libui/PointerController.h index 5e531e9ba8e5..eb48d9a1f6c0 100644 --- a/widget/gonk/libui/PointerController.h +++ b/widget/gonk/libui/PointerController.h @@ -19,11 +19,12 @@ #include "SpriteController.h" -#include "DisplayInfo.h" +#include #include "Input.h" +#include #include #include -#include "String8.h" +#include #include @@ -141,11 +142,7 @@ public: * * Handles pointer acceleration and animation. */ -#ifdef HAVE_ANDROID_OS class PointerController : public PointerControllerInterface, public MessageHandler { -#else -class PointerController : public PointerControllerInterface { -#endif protected: virtual ~PointerController(); @@ -173,8 +170,7 @@ public: const uint32_t* spotIdToIndex, BitSet32 spotIdBits); virtual void clearSpots(); - void setDisplaySize(int32_t width, int32_t height); - void setDisplayOrientation(int32_t orientation); + void setDisplayViewport(int32_t width, int32_t height, int32_t orientation); void setPointerIcon(const SpriteIcon& icon); void setInactivityTimeout(InactivityTimeout inactivityTimeout); @@ -211,9 +207,7 @@ private: sp mPolicy; sp mLooper; sp mSpriteController; -#ifdef HAVE_ANDROID_OS sp mHandler; -#endif PointerResources mResources; @@ -247,9 +241,7 @@ private: bool getBoundsLocked(float* outMinX, float* outMinY, float* outMaxX, float* outMaxY) const; void setPositionLocked(float x, float y); -#ifdef HAVE_ANDROID_OS void handleMessage(const Message& message); -#endif void doAnimate(); void doInactivityTimeout(); diff --git a/widget/gonk/libui/PowerManager.h b/widget/gonk/libui/PowerManager.h index dd80318e6349..ba98db07ca69 100644 --- a/widget/gonk/libui/PowerManager.h +++ b/widget/gonk/libui/PowerManager.h @@ -14,20 +14,20 @@ * limitations under the License. */ -#ifndef _UI_POWER_MANAGER_H -#define _UI_POWER_MANAGER_H +#ifndef _ANDROIDFW_POWER_MANAGER_H +#define _ANDROIDFW_POWER_MANAGER_H namespace android { enum { - POWER_MANAGER_OTHER_EVENT = 0, - POWER_MANAGER_BUTTON_EVENT = 1, - POWER_MANAGER_TOUCH_EVENT = 2, + USER_ACTIVITY_EVENT_OTHER = 0, + USER_ACTIVITY_EVENT_BUTTON = 1, + USER_ACTIVITY_EVENT_TOUCH = 2, - POWER_MANAGER_LAST_EVENT = POWER_MANAGER_TOUCH_EVENT, // Last valid event code. + USER_ACTIVITY_EVENT_LAST = USER_ACTIVITY_EVENT_TOUCH, // Last valid event code. }; } // namespace android -#endif // _UI_POWER_MANAGER_H +#endif // _ANDROIDFW_POWER_MANAGER_H diff --git a/widget/gonk/libui/PropertyMap.cpp b/widget/gonk/libui/PropertyMap.cpp deleted file mode 100644 index 4775a0d151ee..000000000000 --- a/widget/gonk/libui/PropertyMap.cpp +++ /dev/null @@ -1,218 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define LOG_TAG "PropertyMap" - -#include -#include - -#include "utils_Log.h" -#include "PropertyMap.h" - -// Enables debug output for the parser. -#define DEBUG_PARSER 0 - -// Enables debug output for parser performance. -#define DEBUG_PARSER_PERFORMANCE 0 - - -namespace android { - -static const char* WHITESPACE = " \t\r"; -static const char* WHITESPACE_OR_PROPERTY_DELIMITER = " \t\r="; - - -// --- PropertyMap --- - -PropertyMap::PropertyMap() { -} - -PropertyMap::~PropertyMap() { -} - -void PropertyMap::clear() { - mProperties.clear(); -} - -void PropertyMap::addProperty(const String8& key, const String8& value) { - mProperties.add(key, value); -} - -bool PropertyMap::hasProperty(const String8& key) const { - return mProperties.indexOfKey(key) >= 0; -} - -bool PropertyMap::tryGetProperty(const String8& key, String8& outValue) const { - ssize_t index = mProperties.indexOfKey(key); - if (index < 0) { - return false; - } - - outValue = mProperties.valueAt(index); - return true; -} - -bool PropertyMap::tryGetProperty(const String8& key, bool& outValue) const { - int32_t intValue; - if (!tryGetProperty(key, intValue)) { - return false; - } - - outValue = intValue; - return true; -} - -bool PropertyMap::tryGetProperty(const String8& key, int32_t& outValue) const { - String8 stringValue; - if (! tryGetProperty(key, stringValue) || stringValue.length() == 0) { - return false; - } - - char* end; - int value = strtol(stringValue.string(), & end, 10); - if (*end != '\0') { - ALOGW("Property key '%s' has invalid value '%s'. Expected an integer.", - key.string(), stringValue.string()); - return false; - } - outValue = value; - return true; -} - -bool PropertyMap::tryGetProperty(const String8& key, float& outValue) const { - String8 stringValue; - if (! tryGetProperty(key, stringValue) || stringValue.length() == 0) { - return false; - } - - char* end; - float value = strtof(stringValue.string(), & end); - if (*end != '\0') { - ALOGW("Property key '%s' has invalid value '%s'. Expected a float.", - key.string(), stringValue.string()); - return false; - } - outValue = value; - return true; -} - -void PropertyMap::addAll(const PropertyMap* map) { - for (size_t i = 0; i < map->mProperties.size(); i++) { - mProperties.add(map->mProperties.keyAt(i), map->mProperties.valueAt(i)); - } -} - -status_t PropertyMap::load(const String8& filename, PropertyMap** outMap) { - *outMap = NULL; - - Tokenizer* tokenizer; - status_t status = Tokenizer::open(filename, &tokenizer); - if (status) { - ALOGE("Error %d opening property file %s.", status, filename.string()); - } else { - PropertyMap* map = new PropertyMap(); - if (!map) { - ALOGE("Error allocating property map."); - status = NO_MEMORY; - } else { -#if DEBUG_PARSER_PERFORMANCE - nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC); -#endif - Parser parser(map, tokenizer); - status = parser.parse(); -#if DEBUG_PARSER_PERFORMANCE - nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime; - ALOGD("Parsed property file '%s' %d lines in %0.3fms.", - tokenizer->getFilename().string(), tokenizer->getLineNumber(), - elapsedTime / 1000000.0); -#endif - if (status) { - delete map; - } else { - *outMap = map; - } - } - delete tokenizer; - } - return status; -} - - -// --- PropertyMap::Parser --- - -PropertyMap::Parser::Parser(PropertyMap* map, Tokenizer* tokenizer) : - mMap(map), mTokenizer(tokenizer) { -} - -PropertyMap::Parser::~Parser() { -} - -status_t PropertyMap::Parser::parse() { - while (!mTokenizer->isEof()) { -#if DEBUG_PARSER - ALOGD("Parsing %s: '%s'.", mTokenizer->getLocation().string(), - mTokenizer->peekRemainderOfLine().string()); -#endif - - mTokenizer->skipDelimiters(WHITESPACE); - - if (!mTokenizer->isEol() && mTokenizer->peekChar() != '#') { - String8 keyToken = mTokenizer->nextToken(WHITESPACE_OR_PROPERTY_DELIMITER); - if (keyToken.isEmpty()) { - ALOGE("%s: Expected non-empty property key.", mTokenizer->getLocation().string()); - return BAD_VALUE; - } - - mTokenizer->skipDelimiters(WHITESPACE); - - if (mTokenizer->nextChar() != '=') { - ALOGE("%s: Expected '=' between property key and value.", - mTokenizer->getLocation().string()); - return BAD_VALUE; - } - - mTokenizer->skipDelimiters(WHITESPACE); - - String8 valueToken = mTokenizer->nextToken(WHITESPACE); - if (valueToken.find("\\", 0) >= 0 || valueToken.find("\"", 0) >= 0) { - ALOGE("%s: Found reserved character '\\' or '\"' in property value.", - mTokenizer->getLocation().string()); - return BAD_VALUE; - } - - mTokenizer->skipDelimiters(WHITESPACE); - if (!mTokenizer->isEol()) { - ALOGE("%s: Expected end of line, got '%s'.", - mTokenizer->getLocation().string(), - mTokenizer->peekRemainderOfLine().string()); - return BAD_VALUE; - } - - if (mMap->hasProperty(keyToken)) { - ALOGE("%s: Duplicate property value for key '%s'.", - mTokenizer->getLocation().string(), keyToken.string()); - return BAD_VALUE; - } - - mMap->addProperty(keyToken, valueToken); - } - - mTokenizer->nextLine(); - } - return NO_ERROR; -} - -} // namespace android diff --git a/widget/gonk/libui/PropertyMap.h b/widget/gonk/libui/PropertyMap.h deleted file mode 100644 index bf012e966337..000000000000 --- a/widget/gonk/libui/PropertyMap.h +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef _UTILS_PROPERTY_MAP_H -#define _UTILS_PROPERTY_MAP_H - -#include -#include "String8.h" -#include -#include "Tokenizer.h" - -namespace android { - -/* - * Provides a mechanism for passing around string-based property key / value pairs - * and loading them from property files. - * - * The property files have the following simple structure: - * - * # Comment - * key = value - * - * Keys and values are any sequence of printable ASCII characters. - * The '=' separates the key from the value. - * The key and value may not contain whitespace. - * - * The '\' character is reserved for escape sequences and is not currently supported. - * The '"" character is reserved for quoting and is not currently supported. - * Files that contain the '\' or '"' character will fail to parse. - * - * The file must not contain duplicate keys. - * - * TODO Support escape sequences and quoted values when needed. - */ -class PropertyMap { -public: - /* Creates an empty property map. */ - PropertyMap(); - ~PropertyMap(); - - /* Clears the property map. */ - void clear(); - - /* Adds a property. - * Replaces the property with the same key if it is already present. - */ - void addProperty(const String8& key, const String8& value); - - /* Returns true if the property map contains the specified key. */ - bool hasProperty(const String8& key) const; - - /* Gets the value of a property and parses it. - * Returns true and sets outValue if the key was found and its value was parsed successfully. - * Otherwise returns false and does not modify outValue. (Also logs a warning.) - */ - bool tryGetProperty(const String8& key, String8& outValue) const; - bool tryGetProperty(const String8& key, bool& outValue) const; - bool tryGetProperty(const String8& key, int32_t& outValue) const; - bool tryGetProperty(const String8& key, float& outValue) const; - - /* Adds all values from the specified property map. */ - void addAll(const PropertyMap* map); - - /* Gets the underlying property map. */ - inline const KeyedVector& getProperties() const { return mProperties; } - - /* Loads a property map from a file. */ - static status_t load(const String8& filename, PropertyMap** outMap); - -private: - class Parser { - PropertyMap* mMap; - Tokenizer* mTokenizer; - - public: - Parser(PropertyMap* map, Tokenizer* tokenizer); - ~Parser(); - status_t parse(); - - private: - status_t parseType(); - status_t parseKey(); - status_t parseKeyProperty(); - status_t parseModifier(const String8& token, int32_t* outMetaState); - status_t parseCharacterLiteral(char16_t* outCharacter); - }; - - KeyedVector mProperties; -}; - -} // namespace android - -#endif // _UTILS_PROPERTY_MAP_H diff --git a/widget/gonk/libui/SpriteController.cpp b/widget/gonk/libui/SpriteController.cpp index 0a5db541ad36..38f198cbf3f9 100644 --- a/widget/gonk/libui/SpriteController.cpp +++ b/widget/gonk/libui/SpriteController.cpp @@ -21,13 +21,17 @@ #include "SpriteController.h" #include "cutils_log.h" -#include "String8.h" +#include +#ifdef HAVE_ANDROID_OS +#include +#endif #include #include #include #include #include +#include namespace android { @@ -214,33 +218,32 @@ void SpriteController::doUpdateSprites() { if (update.state.surfaceControl != NULL && !update.state.surfaceDrawn && update.state.wantSurfaceVisible()) { sp surface = update.state.surfaceControl->getSurface(); - Surface::SurfaceInfo surfaceInfo; - status_t status = surface->lock(&surfaceInfo); + ANativeWindow_Buffer outBuffer; + status_t status = surface->lock(&outBuffer, NULL); if (status) { ALOGE("Error %d locking sprite surface before drawing.", status); } else { SkBitmap surfaceBitmap; - ssize_t bpr = surfaceInfo.s * bytesPerPixel(surfaceInfo.format); + ssize_t bpr = outBuffer.stride * bytesPerPixel(outBuffer.format); surfaceBitmap.setConfig(SkBitmap::kARGB_8888_Config, - surfaceInfo.w, surfaceInfo.h, bpr); - surfaceBitmap.setPixels(surfaceInfo.bits); + outBuffer.width, outBuffer.height, bpr); + surfaceBitmap.setPixels(outBuffer.bits); - SkCanvas surfaceCanvas; - surfaceCanvas.setBitmapDevice(surfaceBitmap); + SkCanvas surfaceCanvas(surfaceBitmap); SkPaint paint; paint.setXfermodeMode(SkXfermode::kSrc_Mode); surfaceCanvas.drawBitmap(update.state.icon.bitmap, 0, 0, &paint); - if (surfaceInfo.w > uint32_t(update.state.icon.bitmap.width())) { + if (outBuffer.width > uint32_t(update.state.icon.bitmap.width())) { paint.setColor(0); // transparent fill color surfaceCanvas.drawRectCoords(update.state.icon.bitmap.width(), 0, - surfaceInfo.w, update.state.icon.bitmap.height(), paint); + outBuffer.width, update.state.icon.bitmap.height(), paint); } - if (surfaceInfo.h > uint32_t(update.state.icon.bitmap.height())) { + if (outBuffer.height > uint32_t(update.state.icon.bitmap.height())) { paint.setColor(0); // transparent fill color surfaceCanvas.drawRectCoords(0, update.state.icon.bitmap.height(), - surfaceInfo.w, surfaceInfo.h, paint); + outBuffer.width, outBuffer.height, paint); } status = surface->unlockAndPost(); @@ -317,7 +320,7 @@ void SpriteController::doUpdateSprites() { } if (becomingVisible) { - status = update.state.surfaceControl->show(surfaceLayer); + status = update.state.surfaceControl->show(); if (status) { ALOGE("Error %d showing sprite surface.", status); } else { @@ -398,9 +401,9 @@ sp SpriteController::obtainSurface(int32_t width, int32_t height ensureSurfaceComposerClient(); sp surfaceControl = mSurfaceComposerClient->createSurface( - String8("Sprite"), 0, width, height, PIXEL_FORMAT_RGBA_8888); - if (surfaceControl == NULL || !surfaceControl->isValid() - || !surfaceControl->getSurface()->isValid()) { + String8("Sprite"), width, height, PIXEL_FORMAT_RGBA_8888, + ISurfaceComposerClient::eHidden); + if (surfaceControl == NULL || !surfaceControl->isValid()) { ALOGE("Error creating sprite surface."); return NULL; } diff --git a/widget/gonk/libui/SpriteController.h b/widget/gonk/libui/SpriteController.h index 8e11109e3e4c..4926095ec800 100644 --- a/widget/gonk/libui/SpriteController.h +++ b/widget/gonk/libui/SpriteController.h @@ -21,12 +21,10 @@ #include #ifdef HAVE_ANDROID_OS -#include -#include -#include +#include +#endif #include -#endif namespace android { @@ -151,11 +149,7 @@ public: * * Clients are responsible for animating sprites by periodically updating their properties. */ -#ifdef HAVE_ANDROID_OS class SpriteController : public MessageHandler { -#else -class SpriteController : public virtual RefBase { -#endif protected: virtual ~SpriteController(); diff --git a/widget/gonk/libui/Static.cpp b/widget/gonk/libui/Static.cpp deleted file mode 100644 index d8e39d0536df..000000000000 --- a/widget/gonk/libui/Static.cpp +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// All static variables go here, to control initialization and -// destruction order in the library. - -#include "Static.h" - -#include -#include "utils_Log.h" - -namespace android { - -class LibUtilsFirstStatics -{ -public: - LibUtilsFirstStatics() - { - initialize_string8(); - initialize_string16(); - } - - ~LibUtilsFirstStatics() - { - terminate_string16(); - terminate_string8(); - } -}; - -static LibUtilsFirstStatics gFirstStatics; -int gDarwinCantLoadAllObjects = 1; - -// ------------ Text output streams -#if 0 -Vector gTextBuffers; - -class LogTextOutput : public BufferedTextOutput -{ -public: - LogTextOutput() : BufferedTextOutput(MULTITHREADED) { } - virtual ~LogTextOutput() { }; - -protected: - virtual status_t writeLines(const struct iovec& vec, size_t N) - { - //android_writevLog(&vec, N); <-- this is now a no-op - if (N != 1) ALOGI("WARNING: writeLines N=%d\n", N); - ALOGI("%.*s", vec.iov_len, (const char*) vec.iov_base); - return NO_ERROR; - } -}; - -class FdTextOutput : public BufferedTextOutput -{ -public: - FdTextOutput(int fd) : BufferedTextOutput(MULTITHREADED), mFD(fd) { } - virtual ~FdTextOutput() { }; - -protected: - virtual status_t writeLines(const struct iovec& vec, size_t N) - { - writev(mFD, &vec, N); - return NO_ERROR; - } - -private: - int mFD; -}; - -static LogTextOutput gLogTextOutput; -static FdTextOutput gStdoutTextOutput(STDOUT_FILENO); -static FdTextOutput gStderrTextOutput(STDERR_FILENO); - -TextOutput& alog(gLogTextOutput); -TextOutput& aout(gStdoutTextOutput); -TextOutput& aerr(gStderrTextOutput); -#endif -} // namespace android diff --git a/widget/gonk/libui/Static.h b/widget/gonk/libui/Static.h deleted file mode 100644 index 309ff27a6ac2..000000000000 --- a/widget/gonk/libui/Static.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// All static variables go here, to control initialization and -// destruction order in the library. - -#include -#include - -namespace android { -// For TextStream.cpp -//extern Vector gTextBuffers; - -// For String8.cpp -extern void initialize_string8(); -extern void terminate_string8(); - -// For String16.cpp -extern void initialize_string16(); -extern void terminate_string16(); - -} // namespace android diff --git a/widget/gonk/libui/String16.cpp b/widget/gonk/libui/String16.cpp deleted file mode 100644 index ac41219761dc..000000000000 --- a/widget/gonk/libui/String16.cpp +++ /dev/null @@ -1,418 +0,0 @@ -/* - * Copyright (C) 2005 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "String16.h" - -#include -#include "utils_Log.h" -#include "Unicode.h" -#include "String8.h" -#include -#include - -#include "Static.h" - -#include -#include -#include - - -namespace android { - -static SharedBuffer* gEmptyStringBuf = NULL; -static char16_t* gEmptyString = NULL; - -static inline char16_t* getEmptyString() -{ - gEmptyStringBuf->acquire(); - return gEmptyString; -} - -void initialize_string16() -{ - SharedBuffer* buf = SharedBuffer::alloc(sizeof(char16_t)); - char16_t* str = (char16_t*)buf->data(); - *str = 0; - gEmptyStringBuf = buf; - gEmptyString = str; -} - -void terminate_string16() -{ - SharedBuffer::bufferFromData(gEmptyString)->release(); - gEmptyStringBuf = NULL; - gEmptyString = NULL; -} - -// --------------------------------------------------------------------------- - -static char16_t* allocFromUTF8(const char* u8str, size_t u8len) -{ - if (u8len == 0) return getEmptyString(); - - const uint8_t* u8cur = (const uint8_t*) u8str; - - const ssize_t u16len = utf8_to_utf16_length(u8cur, u8len); - if (u16len < 0) { - return getEmptyString(); - } - - const uint8_t* const u8end = u8cur + u8len; - - SharedBuffer* buf = SharedBuffer::alloc(sizeof(char16_t)*(u16len+1)); - if (buf) { - u8cur = (const uint8_t*) u8str; - char16_t* u16str = (char16_t*)buf->data(); - - utf8_to_utf16(u8cur, u8len, u16str); - - //printf("Created UTF-16 string from UTF-8 \"%s\":", in); - //printHexData(1, str, buf->size(), 16, 1); - //printf("\n"); - - return u16str; - } - - return getEmptyString(); -} - -// --------------------------------------------------------------------------- - -String16::String16() - : mString(getEmptyString()) -{ -} - -String16::String16(const String16& o) - : mString(o.mString) -{ - SharedBuffer::bufferFromData(mString)->acquire(); -} - -String16::String16(const String16& o, size_t len, size_t begin) - : mString(getEmptyString()) -{ - setTo(o, len, begin); -} - -String16::String16(const char16_t* o) -{ - size_t len = strlen16(o); - SharedBuffer* buf = SharedBuffer::alloc((len+1)*sizeof(char16_t)); - ALOG_ASSERT(buf, "Unable to allocate shared buffer"); - if (buf) { - char16_t* str = (char16_t*)buf->data(); - strcpy16(str, o); - mString = str; - return; - } - - mString = getEmptyString(); -} - -String16::String16(const char16_t* o, size_t len) -{ - SharedBuffer* buf = SharedBuffer::alloc((len+1)*sizeof(char16_t)); - ALOG_ASSERT(buf, "Unable to allocate shared buffer"); - if (buf) { - char16_t* str = (char16_t*)buf->data(); - memcpy(str, o, len*sizeof(char16_t)); - str[len] = 0; - mString = str; - return; - } - - mString = getEmptyString(); -} - -String16::String16(const String8& o) - : mString(allocFromUTF8(o.string(), o.size())) -{ -} - -String16::String16(const char* o) - : mString(allocFromUTF8(o, strlen(o))) -{ -} - -String16::String16(const char* o, size_t len) - : mString(allocFromUTF8(o, len)) -{ -} - -String16::~String16() -{ - SharedBuffer::bufferFromData(mString)->release(); -} - -void String16::setTo(const String16& other) -{ - SharedBuffer::bufferFromData(other.mString)->acquire(); - SharedBuffer::bufferFromData(mString)->release(); - mString = other.mString; -} - -status_t String16::setTo(const String16& other, size_t len, size_t begin) -{ - const size_t N = other.size(); - if (begin >= N) { - SharedBuffer::bufferFromData(mString)->release(); - mString = getEmptyString(); - return NO_ERROR; - } - if ((begin+len) > N) len = N-begin; - if (begin == 0 && len == N) { - setTo(other); - return NO_ERROR; - } - - if (&other == this) { - LOG_ALWAYS_FATAL("Not implemented"); - } - - return setTo(other.string()+begin, len); -} - -status_t String16::setTo(const char16_t* other) -{ - return setTo(other, strlen16(other)); -} - -status_t String16::setTo(const char16_t* other, size_t len) -{ - SharedBuffer* buf = SharedBuffer::bufferFromData(mString) - ->editResize((len+1)*sizeof(char16_t)); - if (buf) { - char16_t* str = (char16_t*)buf->data(); - memmove(str, other, len*sizeof(char16_t)); - str[len] = 0; - mString = str; - return NO_ERROR; - } - return NO_MEMORY; -} - -status_t String16::append(const String16& other) -{ - const size_t myLen = size(); - const size_t otherLen = other.size(); - if (myLen == 0) { - setTo(other); - return NO_ERROR; - } else if (otherLen == 0) { - return NO_ERROR; - } - - SharedBuffer* buf = SharedBuffer::bufferFromData(mString) - ->editResize((myLen+otherLen+1)*sizeof(char16_t)); - if (buf) { - char16_t* str = (char16_t*)buf->data(); - memcpy(str+myLen, other, (otherLen+1)*sizeof(char16_t)); - mString = str; - return NO_ERROR; - } - return NO_MEMORY; -} - -status_t String16::append(const char16_t* chrs, size_t otherLen) -{ - const size_t myLen = size(); - if (myLen == 0) { - setTo(chrs, otherLen); - return NO_ERROR; - } else if (otherLen == 0) { - return NO_ERROR; - } - - SharedBuffer* buf = SharedBuffer::bufferFromData(mString) - ->editResize((myLen+otherLen+1)*sizeof(char16_t)); - if (buf) { - char16_t* str = (char16_t*)buf->data(); - memcpy(str+myLen, chrs, otherLen*sizeof(char16_t)); - str[myLen+otherLen] = 0; - mString = str; - return NO_ERROR; - } - return NO_MEMORY; -} - -status_t String16::insert(size_t pos, const char16_t* chrs) -{ - return insert(pos, chrs, strlen16(chrs)); -} - -status_t String16::insert(size_t pos, const char16_t* chrs, size_t len) -{ - const size_t myLen = size(); - if (myLen == 0) { - return setTo(chrs, len); - return NO_ERROR; - } else if (len == 0) { - return NO_ERROR; - } - - if (pos > myLen) pos = myLen; - - #if 0 - printf("Insert in to %s: pos=%d, len=%d, myLen=%d, chrs=%s\n", - String8(*this).string(), pos, - len, myLen, String8(chrs, len).string()); - #endif - - SharedBuffer* buf = SharedBuffer::bufferFromData(mString) - ->editResize((myLen+len+1)*sizeof(char16_t)); - if (buf) { - char16_t* str = (char16_t*)buf->data(); - if (pos < myLen) { - memmove(str+pos+len, str+pos, (myLen-pos)*sizeof(char16_t)); - } - memcpy(str+pos, chrs, len*sizeof(char16_t)); - str[myLen+len] = 0; - mString = str; - #if 0 - printf("Result (%d chrs): %s\n", size(), String8(*this).string()); - #endif - return NO_ERROR; - } - return NO_MEMORY; -} - -ssize_t String16::findFirst(char16_t c) const -{ - const char16_t* str = string(); - const char16_t* p = str; - const char16_t* e = p + size(); - while (p < e) { - if (*p == c) { - return p-str; - } - p++; - } - return -1; -} - -ssize_t String16::findLast(char16_t c) const -{ - const char16_t* str = string(); - const char16_t* p = str; - const char16_t* e = p + size(); - while (p < e) { - e--; - if (*e == c) { - return e-str; - } - } - return -1; -} - -bool String16::startsWith(const String16& prefix) const -{ - const size_t ps = prefix.size(); - if (ps > size()) return false; - return strzcmp16(mString, ps, prefix.string(), ps) == 0; -} - -bool String16::startsWith(const char16_t* prefix) const -{ - const size_t ps = strlen16(prefix); - if (ps > size()) return false; - return strncmp16(mString, prefix, ps) == 0; -} - -status_t String16::makeLower() -{ - const size_t N = size(); - const char16_t* str = string(); - char16_t* edit = NULL; - for (size_t i=0; i= 'A' && v <= 'Z') { - if (!edit) { - SharedBuffer* buf = SharedBuffer::bufferFromData(mString)->edit(); - if (!buf) { - return NO_MEMORY; - } - edit = (char16_t*)buf->data(); - mString = str = edit; - } - edit[i] = tolower((char)v); - } - } - return NO_ERROR; -} - -status_t String16::replaceAll(char16_t replaceThis, char16_t withThis) -{ - const size_t N = size(); - const char16_t* str = string(); - char16_t* edit = NULL; - for (size_t i=0; iedit(); - if (!buf) { - return NO_MEMORY; - } - edit = (char16_t*)buf->data(); - mString = str = edit; - } - edit[i] = withThis; - } - } - return NO_ERROR; -} - -status_t String16::remove(size_t len, size_t begin) -{ - const size_t N = size(); - if (begin >= N) { - SharedBuffer::bufferFromData(mString)->release(); - mString = getEmptyString(); - return NO_ERROR; - } - if ((begin+len) > N) len = N-begin; - if (begin == 0 && len == N) { - return NO_ERROR; - } - - if (begin > 0) { - SharedBuffer* buf = SharedBuffer::bufferFromData(mString) - ->editResize((N+1)*sizeof(char16_t)); - if (!buf) { - return NO_MEMORY; - } - char16_t* str = (char16_t*)buf->data(); - memmove(str, str+begin, (N-begin+1)*sizeof(char16_t)); - mString = str; - } - SharedBuffer* buf = SharedBuffer::bufferFromData(mString) - ->editResize((len+1)*sizeof(char16_t)); - if (buf) { - char16_t* str = (char16_t*)buf->data(); - str[len] = 0; - mString = str; - return NO_ERROR; - } - return NO_MEMORY; -} - -TextOutput& operator<<(TextOutput& to, const String16& val) -{ - to << String8(val).string(); - return to; -} - -}; // namespace android diff --git a/widget/gonk/libui/String16.h b/widget/gonk/libui/String16.h deleted file mode 100644 index 3bca09e23de6..000000000000 --- a/widget/gonk/libui/String16.h +++ /dev/null @@ -1,238 +0,0 @@ -/* - * Copyright (C) 2005 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ANDROID_STRING16_H -#define ANDROID_STRING16_H - -#include -#include -#include "Unicode.h" - -// --------------------------------------------------------------------------- - -extern "C" { - -} - -// --------------------------------------------------------------------------- - -namespace android { - -// --------------------------------------------------------------------------- - -class String8; -class TextOutput; - -//! This is a string holding UTF-16 characters. -class String16 -{ -public: - String16(); - String16(const String16& o); - String16(const String16& o, - size_t len, - size_t begin=0); - explicit String16(const char16_t* o); - explicit String16(const char16_t* o, size_t len); - explicit String16(const String8& o); - explicit String16(const char* o); - explicit String16(const char* o, size_t len); - - ~String16(); - - inline const char16_t* string() const; - inline size_t size() const; - - inline const SharedBuffer* sharedBuffer() const; - - void setTo(const String16& other); - status_t setTo(const char16_t* other); - status_t setTo(const char16_t* other, size_t len); - status_t setTo(const String16& other, - size_t len, - size_t begin=0); - - status_t append(const String16& other); - status_t append(const char16_t* other, size_t len); - - inline String16& operator=(const String16& other); - - inline String16& operator+=(const String16& other); - inline String16 operator+(const String16& other) const; - - status_t insert(size_t pos, const char16_t* chrs); - status_t insert(size_t pos, - const char16_t* chrs, size_t len); - - ssize_t findFirst(char16_t c) const; - ssize_t findLast(char16_t c) const; - - bool startsWith(const String16& prefix) const; - bool startsWith(const char16_t* prefix) const; - - status_t makeLower(); - - status_t replaceAll(char16_t replaceThis, - char16_t withThis); - - status_t remove(size_t len, size_t begin=0); - - inline int compare(const String16& other) const; - - inline bool operator<(const String16& other) const; - inline bool operator<=(const String16& other) const; - inline bool operator==(const String16& other) const; - inline bool operator!=(const String16& other) const; - inline bool operator>=(const String16& other) const; - inline bool operator>(const String16& other) const; - - inline bool operator<(const char16_t* other) const; - inline bool operator<=(const char16_t* other) const; - inline bool operator==(const char16_t* other) const; - inline bool operator!=(const char16_t* other) const; - inline bool operator>=(const char16_t* other) const; - inline bool operator>(const char16_t* other) const; - - inline operator const char16_t*() const; - -private: - const char16_t* mString; -}; - -TextOutput& operator<<(TextOutput& to, const String16& val); - -// --------------------------------------------------------------------------- -// No user servicable parts below. - -inline int compare_type(const String16& lhs, const String16& rhs) -{ - return lhs.compare(rhs); -} - -inline int strictly_order_type(const String16& lhs, const String16& rhs) -{ - return compare_type(lhs, rhs) < 0; -} - -inline const char16_t* String16::string() const -{ - return mString; -} - -inline size_t String16::size() const -{ - return SharedBuffer::sizeFromData(mString)/sizeof(char16_t)-1; -} - -inline const SharedBuffer* String16::sharedBuffer() const -{ - return SharedBuffer::bufferFromData(mString); -} - -inline String16& String16::operator=(const String16& other) -{ - setTo(other); - return *this; -} - -inline String16& String16::operator+=(const String16& other) -{ - append(other); - return *this; -} - -inline String16 String16::operator+(const String16& other) const -{ - String16 tmp(*this); - tmp += other; - return tmp; -} - -inline int String16::compare(const String16& other) const -{ - return strzcmp16(mString, size(), other.mString, other.size()); -} - -inline bool String16::operator<(const String16& other) const -{ - return strzcmp16(mString, size(), other.mString, other.size()) < 0; -} - -inline bool String16::operator<=(const String16& other) const -{ - return strzcmp16(mString, size(), other.mString, other.size()) <= 0; -} - -inline bool String16::operator==(const String16& other) const -{ - return strzcmp16(mString, size(), other.mString, other.size()) == 0; -} - -inline bool String16::operator!=(const String16& other) const -{ - return strzcmp16(mString, size(), other.mString, other.size()) != 0; -} - -inline bool String16::operator>=(const String16& other) const -{ - return strzcmp16(mString, size(), other.mString, other.size()) >= 0; -} - -inline bool String16::operator>(const String16& other) const -{ - return strzcmp16(mString, size(), other.mString, other.size()) > 0; -} - -inline bool String16::operator<(const char16_t* other) const -{ - return strcmp16(mString, other) < 0; -} - -inline bool String16::operator<=(const char16_t* other) const -{ - return strcmp16(mString, other) <= 0; -} - -inline bool String16::operator==(const char16_t* other) const -{ - return strcmp16(mString, other) == 0; -} - -inline bool String16::operator!=(const char16_t* other) const -{ - return strcmp16(mString, other) != 0; -} - -inline bool String16::operator>=(const char16_t* other) const -{ - return strcmp16(mString, other) >= 0; -} - -inline bool String16::operator>(const char16_t* other) const -{ - return strcmp16(mString, other) > 0; -} - -inline String16::operator const char16_t*() const -{ - return mString; -} - -}; // namespace android - -// --------------------------------------------------------------------------- - -#endif // ANDROID_STRING16_H diff --git a/widget/gonk/libui/String8.cpp b/widget/gonk/libui/String8.cpp deleted file mode 100644 index ee3355c2d1ee..000000000000 --- a/widget/gonk/libui/String8.cpp +++ /dev/null @@ -1,636 +0,0 @@ -/* - * Copyright (C) 2005 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "String8.h" - -#include "utils_Log.h" -#include "Unicode.h" -#include -#include "String16.h" -#include -#include - -#include "Static.h" - -#include - -#define OS_PATH_SEPARATOR '/' - -/* - * Functions outside android is below the namespace android, since they use - * functions and constants in android namespace. - */ - -// --------------------------------------------------------------------------- - -namespace android { - -// Separator used by resource paths. This is not platform dependent contrary -// to OS_PATH_SEPARATOR. -#define RES_PATH_SEPARATOR '/' - -static SharedBuffer* gEmptyStringBuf = NULL; -static char* gEmptyString = NULL; - -extern int gDarwinCantLoadAllObjects; -int gDarwinIsReallyAnnoying; - -static inline char* getEmptyString() -{ - gEmptyStringBuf->acquire(); - return gEmptyString; -} - -void initialize_string8() -{ - // HACK: This dummy dependency forces linking libutils Static.cpp, - // which is needed to initialize String8/String16 classes. - // These variables are named for Darwin, but are needed elsewhere too, - // including static linking on any platform. - gDarwinIsReallyAnnoying = gDarwinCantLoadAllObjects; - - SharedBuffer* buf = SharedBuffer::alloc(1); - char* str = (char*)buf->data(); - *str = 0; - gEmptyStringBuf = buf; - gEmptyString = str; -} - -void terminate_string8() -{ - SharedBuffer::bufferFromData(gEmptyString)->release(); - gEmptyStringBuf = NULL; - gEmptyString = NULL; -} - -// --------------------------------------------------------------------------- - -static char* allocFromUTF8(const char* in, size_t len) -{ - if (len > 0) { - SharedBuffer* buf = SharedBuffer::alloc(len+1); - ALOG_ASSERT(buf, "Unable to allocate shared buffer"); - if (buf) { - char* str = (char*)buf->data(); - memcpy(str, in, len); - str[len] = 0; - return str; - } - return NULL; - } - - return getEmptyString(); -} - -static char* allocFromUTF16(const char16_t* in, size_t len) -{ - if (len == 0) return getEmptyString(); - - const ssize_t bytes = utf16_to_utf8_length(in, len); - if (bytes < 0) { - return getEmptyString(); - } - - SharedBuffer* buf = SharedBuffer::alloc(bytes+1); - ALOG_ASSERT(buf, "Unable to allocate shared buffer"); - if (!buf) { - return getEmptyString(); - } - - char* str = (char*)buf->data(); - utf16_to_utf8(in, len, str); - return str; -} - -static char* allocFromUTF32(const char32_t* in, size_t len) -{ - if (len == 0) { - return getEmptyString(); - } - - const ssize_t bytes = utf32_to_utf8_length(in, len); - if (bytes < 0) { - return getEmptyString(); - } - - SharedBuffer* buf = SharedBuffer::alloc(bytes+1); - ALOG_ASSERT(buf, "Unable to allocate shared buffer"); - if (!buf) { - return getEmptyString(); - } - - char* str = (char*) buf->data(); - utf32_to_utf8(in, len, str); - - return str; -} - -// --------------------------------------------------------------------------- - -String8::String8() - : mString(getEmptyString()) -{ -} - -String8::String8(const String8& o) - : mString(o.mString) -{ - SharedBuffer::bufferFromData(mString)->acquire(); -} - -String8::String8(const char* o) - : mString(allocFromUTF8(o, strlen(o))) -{ - if (mString == NULL) { - mString = getEmptyString(); - } -} - -String8::String8(const char* o, size_t len) - : mString(allocFromUTF8(o, len)) -{ - if (mString == NULL) { - mString = getEmptyString(); - } -} - -String8::String8(const String16& o) - : mString(allocFromUTF16(o.string(), o.size())) -{ -} - -String8::String8(const char16_t* o) - : mString(allocFromUTF16(o, strlen16(o))) -{ -} - -String8::String8(const char16_t* o, size_t len) - : mString(allocFromUTF16(o, len)) -{ -} - -String8::String8(const char32_t* o) - : mString(allocFromUTF32(o, strlen32(o))) -{ -} - -String8::String8(const char32_t* o, size_t len) - : mString(allocFromUTF32(o, len)) -{ -} - -String8::~String8() -{ - SharedBuffer::bufferFromData(mString)->release(); -} - -String8 String8::format(const char* fmt, ...) -{ - va_list args; - va_start(args, fmt); - - String8 result(formatV(fmt, args)); - - va_end(args); - return result; -} - -String8 String8::formatV(const char* fmt, va_list args) -{ - String8 result; - result.appendFormatV(fmt, args); - return result; -} - -void String8::clear() { - SharedBuffer::bufferFromData(mString)->release(); - mString = getEmptyString(); -} - -void String8::setTo(const String8& other) -{ - SharedBuffer::bufferFromData(other.mString)->acquire(); - SharedBuffer::bufferFromData(mString)->release(); - mString = other.mString; -} - -status_t String8::setTo(const char* other) -{ - const char *newString = allocFromUTF8(other, strlen(other)); - SharedBuffer::bufferFromData(mString)->release(); - mString = newString; - if (mString) return NO_ERROR; - - mString = getEmptyString(); - return NO_MEMORY; -} - -status_t String8::setTo(const char* other, size_t len) -{ - const char *newString = allocFromUTF8(other, len); - SharedBuffer::bufferFromData(mString)->release(); - mString = newString; - if (mString) return NO_ERROR; - - mString = getEmptyString(); - return NO_MEMORY; -} - -status_t String8::setTo(const char16_t* other, size_t len) -{ - const char *newString = allocFromUTF16(other, len); - SharedBuffer::bufferFromData(mString)->release(); - mString = newString; - if (mString) return NO_ERROR; - - mString = getEmptyString(); - return NO_MEMORY; -} - -status_t String8::setTo(const char32_t* other, size_t len) -{ - const char *newString = allocFromUTF32(other, len); - SharedBuffer::bufferFromData(mString)->release(); - mString = newString; - if (mString) return NO_ERROR; - - mString = getEmptyString(); - return NO_MEMORY; -} - -status_t String8::append(const String8& other) -{ - const size_t otherLen = other.bytes(); - if (bytes() == 0) { - setTo(other); - return NO_ERROR; - } else if (otherLen == 0) { - return NO_ERROR; - } - - return real_append(other.string(), otherLen); -} - -status_t String8::append(const char* other) -{ - return append(other, strlen(other)); -} - -status_t String8::append(const char* other, size_t otherLen) -{ - if (bytes() == 0) { - return setTo(other, otherLen); - } else if (otherLen == 0) { - return NO_ERROR; - } - - return real_append(other, otherLen); -} - -status_t String8::appendFormat(const char* fmt, ...) -{ - va_list args; - va_start(args, fmt); - - status_t result = appendFormatV(fmt, args); - - va_end(args); - return result; -} - -status_t String8::appendFormatV(const char* fmt, va_list args) -{ - int result = NO_ERROR; - int n = vsnprintf(NULL, 0, fmt, args); - if (n != 0) { - size_t oldLength = length(); - char* buf = lockBuffer(oldLength + n); - if (buf) { - vsnprintf(buf + oldLength, n + 1, fmt, args); - } else { - result = NO_MEMORY; - } - } - return result; -} - -status_t String8::real_append(const char* other, size_t otherLen) -{ - const size_t myLen = bytes(); - - SharedBuffer* buf = SharedBuffer::bufferFromData(mString) - ->editResize(myLen+otherLen+1); - if (buf) { - char* str = (char*)buf->data(); - mString = str; - str += myLen; - memcpy(str, other, otherLen); - str[otherLen] = '\0'; - return NO_ERROR; - } - return NO_MEMORY; -} - -char* String8::lockBuffer(size_t size) -{ - SharedBuffer* buf = SharedBuffer::bufferFromData(mString) - ->editResize(size+1); - if (buf) { - char* str = (char*)buf->data(); - mString = str; - return str; - } - return NULL; -} - -void String8::unlockBuffer() -{ - unlockBuffer(strlen(mString)); -} - -status_t String8::unlockBuffer(size_t size) -{ - if (size != this->size()) { - SharedBuffer* buf = SharedBuffer::bufferFromData(mString) - ->editResize(size+1); - if (! buf) { - return NO_MEMORY; - } - - char* str = (char*)buf->data(); - str[size] = 0; - mString = str; - } - - return NO_ERROR; -} - -ssize_t String8::find(const char* other, size_t start) const -{ - size_t len = size(); - if (start >= len) { - return -1; - } - const char* s = mString+start; - const char* p = strstr(s, other); - return p ? p-mString : -1; -} - -void String8::toLower() -{ - toLower(0, size()); -} - -void String8::toLower(size_t start, size_t length) -{ - const size_t len = size(); - if (start >= len) { - return; - } - if (start+length > len) { - length = len-start; - } - char* buf = lockBuffer(len); - buf += start; - while (length > 0) { - *buf = tolower(*buf); - buf++; - length--; - } - unlockBuffer(len); -} - -void String8::toUpper() -{ - toUpper(0, size()); -} - -void String8::toUpper(size_t start, size_t length) -{ - const size_t len = size(); - if (start >= len) { - return; - } - if (start+length > len) { - length = len-start; - } - char* buf = lockBuffer(len); - buf += start; - while (length > 0) { - *buf = toupper(*buf); - buf++; - length--; - } - unlockBuffer(len); -} - -size_t String8::getUtf32Length() const -{ - return utf8_to_utf32_length(mString, length()); -} - -int32_t String8::getUtf32At(size_t index, size_t *next_index) const -{ - return utf32_from_utf8_at(mString, length(), index, next_index); -} - -void String8::getUtf32(char32_t* dst) const -{ - utf8_to_utf32(mString, length(), dst); -} - -TextOutput& operator<<(TextOutput& to, const String8& val) -{ - to << val.string(); - return to; -} - -// --------------------------------------------------------------------------- -// Path functions - -void String8::setPathName(const char* name) -{ - setPathName(name, strlen(name)); -} - -void String8::setPathName(const char* name, size_t len) -{ - char* buf = lockBuffer(len); - - memcpy(buf, name, len); - - // remove trailing path separator, if present - if (len > 0 && buf[len-1] == OS_PATH_SEPARATOR) - len--; - - buf[len] = '\0'; - - unlockBuffer(len); -} - -String8 String8::getPathLeaf(void) const -{ - const char* cp; - const char*const buf = mString; - - cp = strrchr(buf, OS_PATH_SEPARATOR); - if (cp == NULL) - return String8(*this); - else - return String8(cp+1); -} - -String8 String8::getPathDir(void) const -{ - const char* cp; - const char*const str = mString; - - cp = strrchr(str, OS_PATH_SEPARATOR); - if (cp == NULL) - return String8(""); - else - return String8(str, cp - str); -} - -String8 String8::walkPath(String8* outRemains) const -{ - const char* cp; - const char*const str = mString; - const char* buf = str; - - cp = strchr(buf, OS_PATH_SEPARATOR); - if (cp == buf) { - // don't include a leading '/'. - buf = buf+1; - cp = strchr(buf, OS_PATH_SEPARATOR); - } - - if (cp == NULL) { - String8 res = buf != str ? String8(buf) : *this; - if (outRemains) *outRemains = String8(""); - return res; - } - - String8 res(buf, cp-buf); - if (outRemains) *outRemains = String8(cp+1); - return res; -} - -/* - * Helper function for finding the start of an extension in a pathname. - * - * Returns a pointer inside mString, or NULL if no extension was found. - */ -char* String8::find_extension(void) const -{ - const char* lastSlash; - const char* lastDot; - int extLen; - const char* const str = mString; - - // only look at the filename - lastSlash = strrchr(str, OS_PATH_SEPARATOR); - if (lastSlash == NULL) - lastSlash = str; - else - lastSlash++; - - // find the last dot - lastDot = strrchr(lastSlash, '.'); - if (lastDot == NULL) - return NULL; - - // looks good, ship it - return const_cast(lastDot); -} - -String8 String8::getPathExtension(void) const -{ - char* ext; - - ext = find_extension(); - if (ext != NULL) - return String8(ext); - else - return String8(""); -} - -String8 String8::getBasePath(void) const -{ - char* ext; - const char* const str = mString; - - ext = find_extension(); - if (ext == NULL) - return String8(*this); - else - return String8(str, ext - str); -} - -String8& String8::appendPath(const char* name) -{ - // TODO: The test below will fail for Win32 paths. Fix later or ignore. - if (name[0] != OS_PATH_SEPARATOR) { - if (*name == '\0') { - // nothing to do - return *this; - } - - size_t len = length(); - if (len == 0) { - // no existing filename, just use the new one - setPathName(name); - return *this; - } - - // make room for oldPath + '/' + newPath - int newlen = strlen(name); - - char* buf = lockBuffer(len+1+newlen); - - // insert a '/' if needed - if (buf[len-1] != OS_PATH_SEPARATOR) - buf[len++] = OS_PATH_SEPARATOR; - - memcpy(buf+len, name, newlen+1); - len += newlen; - - unlockBuffer(len); - - return *this; - } else { - setPathName(name); - return *this; - } -} - -String8& String8::convertToResPath() -{ -#if OS_PATH_SEPARATOR != RES_PATH_SEPARATOR - size_t len = length(); - if (len > 0) { - char * buf = lockBuffer(len); - for (char * end = buf + len; buf < end; ++buf) { - if (*buf == OS_PATH_SEPARATOR) - *buf = RES_PATH_SEPARATOR; - } - unlockBuffer(len); - } -#endif - return *this; -} - -}; // namespace android diff --git a/widget/gonk/libui/String8.h b/widget/gonk/libui/String8.h deleted file mode 100644 index 28cc9f1f8de8..000000000000 --- a/widget/gonk/libui/String8.h +++ /dev/null @@ -1,383 +0,0 @@ -/* - * Copyright (C) 2005 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ANDROID_STRING8_H -#define ANDROID_STRING8_H - -#include -#include -#include "Unicode.h" - -#include // for strcmp -#include - -// --------------------------------------------------------------------------- - -namespace android { - -class String16; -class TextOutput; - -//! This is a string holding UTF-8 characters. Does not allow the value more -// than 0x10FFFF, which is not valid unicode codepoint. -class String8 -{ -public: - String8(); - String8(const String8& o); - explicit String8(const char* o); - explicit String8(const char* o, size_t numChars); - - explicit String8(const String16& o); - explicit String8(const char16_t* o); - explicit String8(const char16_t* o, size_t numChars); - explicit String8(const char32_t* o); - explicit String8(const char32_t* o, size_t numChars); - ~String8(); - - static inline const String8 empty(); - - static String8 format(const char* fmt, ...) __attribute__((format (printf, 1, 2))); - static String8 formatV(const char* fmt, va_list args); - - inline const char* string() const; - inline size_t size() const; - inline size_t length() const; - inline size_t bytes() const; - inline bool isEmpty() const; - - inline const SharedBuffer* sharedBuffer() const; - - void clear(); - - void setTo(const String8& other); - status_t setTo(const char* other); - status_t setTo(const char* other, size_t numChars); - status_t setTo(const char16_t* other, size_t numChars); - status_t setTo(const char32_t* other, - size_t length); - - status_t append(const String8& other); - status_t append(const char* other); - status_t append(const char* other, size_t numChars); - - status_t appendFormat(const char* fmt, ...) - __attribute__((format (printf, 2, 3))); - status_t appendFormatV(const char* fmt, va_list args); - - // Note that this function takes O(N) time to calculate the value. - // No cache value is stored. - size_t getUtf32Length() const; - int32_t getUtf32At(size_t index, - size_t *next_index) const; - void getUtf32(char32_t* dst) const; - - inline String8& operator=(const String8& other); - inline String8& operator=(const char* other); - - inline String8& operator+=(const String8& other); - inline String8 operator+(const String8& other) const; - - inline String8& operator+=(const char* other); - inline String8 operator+(const char* other) const; - - inline int compare(const String8& other) const; - - inline bool operator<(const String8& other) const; - inline bool operator<=(const String8& other) const; - inline bool operator==(const String8& other) const; - inline bool operator!=(const String8& other) const; - inline bool operator>=(const String8& other) const; - inline bool operator>(const String8& other) const; - - inline bool operator<(const char* other) const; - inline bool operator<=(const char* other) const; - inline bool operator==(const char* other) const; - inline bool operator!=(const char* other) const; - inline bool operator>=(const char* other) const; - inline bool operator>(const char* other) const; - - inline operator const char*() const; - - char* lockBuffer(size_t size); - void unlockBuffer(); - status_t unlockBuffer(size_t size); - - // return the index of the first byte of other in this at or after - // start, or -1 if not found - ssize_t find(const char* other, size_t start = 0) const; - - void toLower(); - void toLower(size_t start, size_t numChars); - void toUpper(); - void toUpper(size_t start, size_t numChars); - - /* - * These methods operate on the string as if it were a path name. - */ - - /* - * Set the filename field to a specific value. - * - * Normalizes the filename, removing a trailing '/' if present. - */ - void setPathName(const char* name); - void setPathName(const char* name, size_t numChars); - - /* - * Get just the filename component. - * - * "/tmp/foo/bar.c" --> "bar.c" - */ - String8 getPathLeaf(void) const; - - /* - * Remove the last (file name) component, leaving just the directory - * name. - * - * "/tmp/foo/bar.c" --> "/tmp/foo" - * "/tmp" --> "" // ????? shouldn't this be "/" ???? XXX - * "bar.c" --> "" - */ - String8 getPathDir(void) const; - - /* - * Retrieve the front (root dir) component. Optionally also return the - * remaining components. - * - * "/tmp/foo/bar.c" --> "tmp" (remain = "foo/bar.c") - * "/tmp" --> "tmp" (remain = "") - * "bar.c" --> "bar.c" (remain = "") - */ - String8 walkPath(String8* outRemains = NULL) const; - - /* - * Return the filename extension. This is the last '.' and any number - * of characters that follow it. The '.' is included in case we - * decide to expand our definition of what constitutes an extension. - * - * "/tmp/foo/bar.c" --> ".c" - * "/tmp" --> "" - * "/tmp/foo.bar/baz" --> "" - * "foo.jpeg" --> ".jpeg" - * "foo." --> "" - */ - String8 getPathExtension(void) const; - - /* - * Return the path without the extension. Rules for what constitutes - * an extension are described in the comment for getPathExtension(). - * - * "/tmp/foo/bar.c" --> "/tmp/foo/bar" - */ - String8 getBasePath(void) const; - - /* - * Add a component to the pathname. We guarantee that there is - * exactly one path separator between the old path and the new. - * If there is no existing name, we just copy the new name in. - * - * If leaf is a fully qualified path (i.e. starts with '/', it - * replaces whatever was there before. - */ - String8& appendPath(const char* leaf); - String8& appendPath(const String8& leaf) { return appendPath(leaf.string()); } - - /* - * Like appendPath(), but does not affect this string. Returns a new one instead. - */ - String8 appendPathCopy(const char* leaf) const - { String8 p(*this); p.appendPath(leaf); return p; } - String8 appendPathCopy(const String8& leaf) const { return appendPathCopy(leaf.string()); } - - /* - * Converts all separators in this string to /, the default path separator. - * - * If the default OS separator is backslash, this converts all - * backslashes to slashes, in-place. Otherwise it does nothing. - * Returns self. - */ - String8& convertToResPath(); - -private: - status_t real_append(const char* other, size_t numChars); - char* find_extension(void) const; - - const char* mString; -}; - -TextOutput& operator<<(TextOutput& to, const String16& val); - -// --------------------------------------------------------------------------- -// No user servicable parts below. - -inline int compare_type(const String8& lhs, const String8& rhs) -{ - return lhs.compare(rhs); -} - -inline int strictly_order_type(const String8& lhs, const String8& rhs) -{ - return compare_type(lhs, rhs) < 0; -} - -inline const String8 String8::empty() { - return String8(); -} - -inline const char* String8::string() const -{ - return mString; -} - -inline size_t String8::length() const -{ - return SharedBuffer::sizeFromData(mString)-1; -} - -inline size_t String8::size() const -{ - return length(); -} - -inline bool String8::isEmpty() const -{ - return length() == 0; -} - -inline size_t String8::bytes() const -{ - return SharedBuffer::sizeFromData(mString)-1; -} - -inline const SharedBuffer* String8::sharedBuffer() const -{ - return SharedBuffer::bufferFromData(mString); -} - -inline String8& String8::operator=(const String8& other) -{ - setTo(other); - return *this; -} - -inline String8& String8::operator=(const char* other) -{ - setTo(other); - return *this; -} - -inline String8& String8::operator+=(const String8& other) -{ - append(other); - return *this; -} - -inline String8 String8::operator+(const String8& other) const -{ - String8 tmp(*this); - tmp += other; - return tmp; -} - -inline String8& String8::operator+=(const char* other) -{ - append(other); - return *this; -} - -inline String8 String8::operator+(const char* other) const -{ - String8 tmp(*this); - tmp += other; - return tmp; -} - -inline int String8::compare(const String8& other) const -{ - return strcmp(mString, other.mString); -} - -inline bool String8::operator<(const String8& other) const -{ - return strcmp(mString, other.mString) < 0; -} - -inline bool String8::operator<=(const String8& other) const -{ - return strcmp(mString, other.mString) <= 0; -} - -inline bool String8::operator==(const String8& other) const -{ - return strcmp(mString, other.mString) == 0; -} - -inline bool String8::operator!=(const String8& other) const -{ - return strcmp(mString, other.mString) != 0; -} - -inline bool String8::operator>=(const String8& other) const -{ - return strcmp(mString, other.mString) >= 0; -} - -inline bool String8::operator>(const String8& other) const -{ - return strcmp(mString, other.mString) > 0; -} - -inline bool String8::operator<(const char* other) const -{ - return strcmp(mString, other) < 0; -} - -inline bool String8::operator<=(const char* other) const -{ - return strcmp(mString, other) <= 0; -} - -inline bool String8::operator==(const char* other) const -{ - return strcmp(mString, other) == 0; -} - -inline bool String8::operator!=(const char* other) const -{ - return strcmp(mString, other) != 0; -} - -inline bool String8::operator>=(const char* other) const -{ - return strcmp(mString, other) >= 0; -} - -inline bool String8::operator>(const char* other) const -{ - return strcmp(mString, other) > 0; -} - -inline String8::operator const char*() const -{ - return mString; -} - -} // namespace android - -// --------------------------------------------------------------------------- - -#endif // ANDROID_STRING8_H diff --git a/widget/gonk/libui/Timers.cpp b/widget/gonk/libui/Timers.cpp deleted file mode 100644 index 4534615329e6..000000000000 --- a/widget/gonk/libui/Timers.cpp +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Copyright (C) 2005 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// -// Timer functions. -// -#include "utils_Log.h" -#include "Timers.h" - -#include -#include -#include -#include -#include -#include -#include - -#ifdef HAVE_WIN32_THREADS -#include -#endif - -nsecs_t systemTime(int clock) -{ -#if defined(HAVE_POSIX_CLOCKS) - static const clockid_t clocks[] = { - CLOCK_REALTIME, - CLOCK_MONOTONIC, - CLOCK_PROCESS_CPUTIME_ID, - CLOCK_THREAD_CPUTIME_ID - }; - struct timespec t; - t.tv_sec = t.tv_nsec = 0; - clock_gettime(clocks[clock], &t); - return nsecs_t(t.tv_sec)*1000000000LL + t.tv_nsec; -#else - // we don't support the clocks here. - struct timeval t; - t.tv_sec = t.tv_usec = 0; - gettimeofday(&t, NULL); - return nsecs_t(t.tv_sec)*1000000000LL + nsecs_t(t.tv_usec)*1000LL; -#endif -} - -int toMillisecondTimeoutDelay(nsecs_t referenceTime, nsecs_t timeoutTime) -{ - int timeoutDelayMillis; - if (timeoutTime > referenceTime) { - uint64_t timeoutDelay = uint64_t(timeoutTime - referenceTime); - if (timeoutDelay > uint64_t((INT_MAX - 1) * 1000000LL)) { - timeoutDelayMillis = -1; - } else { - timeoutDelayMillis = (timeoutDelay + 999999LL) / 1000000LL; - } - } else { - timeoutDelayMillis = 0; - } - return timeoutDelayMillis; -} - - -/* - * =========================================================================== - * DurationTimer - * =========================================================================== - */ - -using namespace android; - -// Start the timer. -void DurationTimer::start(void) -{ - gettimeofday(&mStartWhen, NULL); -} - -// Stop the timer. -void DurationTimer::stop(void) -{ - gettimeofday(&mStopWhen, NULL); -} - -// Get the duration in microseconds. -long long DurationTimer::durationUsecs(void) const -{ - return (long) subtractTimevals(&mStopWhen, &mStartWhen); -} - -// Subtract two timevals. Returns the difference (ptv1-ptv2) in -// microseconds. -/*static*/ long long DurationTimer::subtractTimevals(const struct timeval* ptv1, - const struct timeval* ptv2) -{ - long long stop = ((long long) ptv1->tv_sec) * 1000000LL + - ((long long) ptv1->tv_usec); - long long start = ((long long) ptv2->tv_sec) * 1000000LL + - ((long long) ptv2->tv_usec); - return stop - start; -} - -// Add the specified amount of time to the timeval. -/*static*/ void DurationTimer::addToTimeval(struct timeval* ptv, long usec) -{ - if (usec < 0) { - ALOG(LOG_WARN, "", "Negative values not supported in addToTimeval\n"); - return; - } - - // normalize tv_usec if necessary - if (ptv->tv_usec >= 1000000) { - ptv->tv_sec += ptv->tv_usec / 1000000; - ptv->tv_usec %= 1000000; - } - - ptv->tv_usec += usec % 1000000; - if (ptv->tv_usec >= 1000000) { - ptv->tv_usec -= 1000000; - ptv->tv_sec++; - } - ptv->tv_sec += usec / 1000000; -} - diff --git a/widget/gonk/libui/Timers.h b/widget/gonk/libui/Timers.h deleted file mode 100644 index 8b4d322873e9..000000000000 --- a/widget/gonk/libui/Timers.h +++ /dev/null @@ -1,144 +0,0 @@ -/* - * Copyright (C) 2005 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// -// Timer functions. -// -#ifndef _LIBS_UTILS_TIMERS_H -#define _LIBS_UTILS_TIMERS_H - -#include -#include -#include - -// ------------------------------------------------------------------ -// C API - -#ifdef __cplusplus -extern "C" { -#endif - -typedef int64_t nsecs_t; // nano-seconds - -static inline nsecs_t seconds_to_nanoseconds(nsecs_t secs) -{ - return secs*1000000000; -} - -static inline nsecs_t milliseconds_to_nanoseconds(nsecs_t secs) -{ - return secs*1000000; -} - -static inline nsecs_t microseconds_to_nanoseconds(nsecs_t secs) -{ - return secs*1000; -} - -static inline nsecs_t nanoseconds_to_seconds(nsecs_t secs) -{ - return secs/1000000000; -} - -static inline nsecs_t nanoseconds_to_milliseconds(nsecs_t secs) -{ - return secs/1000000; -} - -static inline nsecs_t nanoseconds_to_microseconds(nsecs_t secs) -{ - return secs/1000; -} - -static inline nsecs_t s2ns(nsecs_t v) {return seconds_to_nanoseconds(v);} -static inline nsecs_t ms2ns(nsecs_t v) {return milliseconds_to_nanoseconds(v);} -static inline nsecs_t us2ns(nsecs_t v) {return microseconds_to_nanoseconds(v);} -static inline nsecs_t ns2s(nsecs_t v) {return nanoseconds_to_seconds(v);} -static inline nsecs_t ns2ms(nsecs_t v) {return nanoseconds_to_milliseconds(v);} -static inline nsecs_t ns2us(nsecs_t v) {return nanoseconds_to_microseconds(v);} - -static inline nsecs_t seconds(nsecs_t v) { return s2ns(v); } -static inline nsecs_t milliseconds(nsecs_t v) { return ms2ns(v); } -static inline nsecs_t microseconds(nsecs_t v) { return us2ns(v); } - -enum { - SYSTEM_TIME_REALTIME = 0, // system-wide realtime clock - SYSTEM_TIME_MONOTONIC = 1, // monotonic time since unspecified starting point - SYSTEM_TIME_PROCESS = 2, // high-resolution per-process clock - SYSTEM_TIME_THREAD = 3 // high-resolution per-thread clock -}; - -// return the system-time according to the specified clock -#ifdef __cplusplus -nsecs_t systemTime(int clock = SYSTEM_TIME_MONOTONIC); -#else -nsecs_t systemTime(int clock); -#endif // def __cplusplus - -/** - * Returns the number of milliseconds to wait between the reference time and the timeout time. - * If the timeout is in the past relative to the reference time, returns 0. - * If the timeout is more than INT_MAX milliseconds in the future relative to the reference time, - * such as when timeoutTime == LLONG_MAX, returns -1 to indicate an infinite timeout delay. - * Otherwise, returns the difference between the reference time and timeout time - * rounded up to the next millisecond. - */ -int toMillisecondTimeoutDelay(nsecs_t referenceTime, nsecs_t timeoutTime); - -#ifdef __cplusplus -} // extern "C" -#endif - -// ------------------------------------------------------------------ -// C++ API - -#ifdef __cplusplus - -namespace android { -/* - * Time the duration of something. - * - * Includes some timeval manipulation functions. - */ -class DurationTimer { -public: - DurationTimer() {} - ~DurationTimer() {} - - // Start the timer. - void start(); - // Stop the timer. - void stop(); - // Get the duration in microseconds. - long long durationUsecs() const; - - // Subtract two timevals. Returns the difference (ptv1-ptv2) in - // microseconds. - static long long subtractTimevals(const struct timeval* ptv1, - const struct timeval* ptv2); - - // Add the specified amount of time to the timeval. - static void addToTimeval(struct timeval* ptv, long usec); - -private: - struct timeval mStartWhen; - struct timeval mStopWhen; -}; - -}; // android -#endif // def __cplusplus - -#endif // _LIBS_UTILS_TIMERS_H diff --git a/widget/gonk/libui/Tokenizer.cpp b/widget/gonk/libui/Tokenizer.cpp index 3aacd3021236..2f585cb4e151 100644 --- a/widget/gonk/libui/Tokenizer.cpp +++ b/widget/gonk/libui/Tokenizer.cpp @@ -15,6 +15,7 @@ */ #define LOG_TAG "Tokenizer" +#include "cutils_log.h" #include #include @@ -22,7 +23,6 @@ #include #include #include -#include "utils_Log.h" #include "Tokenizer.h" // Enables debug output for the tokenizer. @@ -35,15 +35,18 @@ static inline bool isDelimiter(char ch, const char* delimiters) { return strchr(delimiters, ch) != NULL; } -Tokenizer::Tokenizer(const String8& filename, FileMap* fileMap, char* buffer, size_t length) : +Tokenizer::Tokenizer(const String8& filename, FileMap* fileMap, char* buffer, + bool ownBuffer, size_t length) : mFilename(filename), mFileMap(fileMap), - mBuffer(buffer), mLength(length), mCurrent(buffer), mLineNumber(1) { + mBuffer(buffer), mOwnBuffer(ownBuffer), mLength(length), + mCurrent(buffer), mLineNumber(1) { } Tokenizer::~Tokenizer() { if (mFileMap) { mFileMap->release(); - } else { + } + if (mOwnBuffer) { delete[] mBuffer; } } @@ -65,6 +68,7 @@ status_t Tokenizer::open(const String8& filename, Tokenizer** outTokenizer) { size_t length = size_t(stat.st_size); FileMap* fileMap = new FileMap(); + bool ownBuffer = false; char* buffer; if (fileMap->create(NULL, fd, 0, length, true)) { fileMap->advise(FileMap::SEQUENTIAL); @@ -77,6 +81,7 @@ status_t Tokenizer::open(const String8& filename, Tokenizer** outTokenizer) { // The length we obtained from stat is wrong too (it will always be 4096) // so we must trust that read will read the entire file. buffer = new char[length]; + ownBuffer = true; ssize_t nrd = read(fd, buffer, length); if (nrd < 0) { result = -errno; @@ -89,7 +94,7 @@ status_t Tokenizer::open(const String8& filename, Tokenizer** outTokenizer) { } if (!result) { - *outTokenizer = new Tokenizer(filename, fileMap, buffer, length); + *outTokenizer = new Tokenizer(filename, fileMap, buffer, ownBuffer, length); } } close(fd); @@ -97,6 +102,13 @@ status_t Tokenizer::open(const String8& filename, Tokenizer** outTokenizer) { return result; } +status_t Tokenizer::fromContents(const String8& filename, + const char* contents, Tokenizer** outTokenizer) { + *outTokenizer = new Tokenizer(filename, NULL, + const_cast(contents), false, strlen(contents)); + return OK; +} + String8 Tokenizer::getLocation() const { String8 result; result.appendFormat("%s:%d", mFilename.string(), mLineNumber); diff --git a/widget/gonk/libui/Tokenizer.h b/widget/gonk/libui/Tokenizer.h index 217483058503..bb25f374cb34 100644 --- a/widget/gonk/libui/Tokenizer.h +++ b/widget/gonk/libui/Tokenizer.h @@ -20,7 +20,7 @@ #include #include #include -#include "String8.h" +#include namespace android { @@ -28,7 +28,8 @@ namespace android { * A simple tokenizer for loading and parsing ASCII text files line by line. */ class Tokenizer { - Tokenizer(const String8& filename, FileMap* fileMap, char* buffer, size_t length); + Tokenizer(const String8& filename, FileMap* fileMap, char* buffer, + bool ownBuffer, size_t length); public: ~Tokenizer(); @@ -41,6 +42,15 @@ public: */ static status_t open(const String8& filename, Tokenizer** outTokenizer); + /** + * Prepares to tokenize the contents of a string. + * + * Returns NO_ERROR and a tokenizer for the string, if successful. + * Otherwise returns an error and sets outTokenizer to NULL. + */ + static status_t fromContents(const String8& filename, + const char* contents, Tokenizer** outTokenizer); + /** * Returns true if at the end of the file. */ @@ -111,6 +121,7 @@ private: String8 mFilename; FileMap* mFileMap; char* mBuffer; + bool mOwnBuffer; size_t mLength; const char* mCurrent; diff --git a/widget/gonk/libui/Trace.h b/widget/gonk/libui/Trace.h new file mode 100644 index 000000000000..24fbfb602695 --- /dev/null +++ b/widget/gonk/libui/Trace.h @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_TRACE_H +#define ANDROID_TRACE_H + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "cutils_trace.h" + +// See for more ATRACE_* macros. + +// ATRACE_NAME traces the beginning and end of the current scope. To trace +// the correct start and end times this macro should be declared first in the +// scope body. +#define ATRACE_NAME(name) android::ScopedTrace ___tracer(ATRACE_TAG, name) +// ATRACE_CALL is an ATRACE_NAME that uses the current function name. +#define ATRACE_CALL() ATRACE_NAME(__FUNCTION__) + +namespace android { + +class ScopedTrace { +public: +inline ScopedTrace(uint64_t tag, const char* name) + : mTag(tag) { +#ifdef HAVE_ANDROID_OS + atrace_begin(mTag,name); +#endif +} + +inline ~ScopedTrace() { +#ifdef HAVE_ANDROID_OS + atrace_end(mTag); +#endif +} + +private: + uint64_t mTag; +}; + +}; // namespace android + +#endif // ANDROID_TRACE_H diff --git a/widget/gonk/libui/Unicode.cpp b/widget/gonk/libui/Unicode.cpp deleted file mode 100644 index 711a7957ccc9..000000000000 --- a/widget/gonk/libui/Unicode.cpp +++ /dev/null @@ -1,576 +0,0 @@ -/* - * Copyright (C) 2005 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "Unicode.h" - -#include - -#ifdef HAVE_WINSOCK -# undef nhtol -# undef htonl -# undef nhtos -# undef htons - -# ifdef HAVE_LITTLE_ENDIAN -# define ntohl(x) ( ((x) << 24) | (((x) >> 24) & 255) | (((x) << 8) & 0xff0000) | (((x) >> 8) & 0xff00) ) -# define htonl(x) ntohl(x) -# define ntohs(x) ( (((x) << 8) & 0xff00) | (((x) >> 8) & 255) ) -# define htons(x) ntohs(x) -# else -# define ntohl(x) (x) -# define htonl(x) (x) -# define ntohs(x) (x) -# define htons(x) (x) -# endif -#else -# include -#endif - -extern "C" { - -static const char32_t kByteMask = 0x000000BF; -static const char32_t kByteMark = 0x00000080; - -// Surrogates aren't valid for UTF-32 characters, so define some -// constants that will let us screen them out. -static const char32_t kUnicodeSurrogateHighStart = 0x0000D800; -static const char32_t kUnicodeSurrogateHighEnd = 0x0000DBFF; -static const char32_t kUnicodeSurrogateLowStart = 0x0000DC00; -static const char32_t kUnicodeSurrogateLowEnd = 0x0000DFFF; -static const char32_t kUnicodeSurrogateStart = kUnicodeSurrogateHighStart; -static const char32_t kUnicodeSurrogateEnd = kUnicodeSurrogateLowEnd; -static const char32_t kUnicodeMaxCodepoint = 0x0010FFFF; - -// Mask used to set appropriate bits in first byte of UTF-8 sequence, -// indexed by number of bytes in the sequence. -// 0xxxxxxx -// -> (00-7f) 7bit. Bit mask for the first byte is 0x00000000 -// 110yyyyx 10xxxxxx -// -> (c0-df)(80-bf) 11bit. Bit mask is 0x000000C0 -// 1110yyyy 10yxxxxx 10xxxxxx -// -> (e0-ef)(80-bf)(80-bf) 16bit. Bit mask is 0x000000E0 -// 11110yyy 10yyxxxx 10xxxxxx 10xxxxxx -// -> (f0-f7)(80-bf)(80-bf)(80-bf) 21bit. Bit mask is 0x000000F0 -static const char32_t kFirstByteMark[] = { - 0x00000000, 0x00000000, 0x000000C0, 0x000000E0, 0x000000F0 -}; - -// -------------------------------------------------------------------------- -// UTF-32 -// -------------------------------------------------------------------------- - -/** - * Return number of UTF-8 bytes required for the character. If the character - * is invalid, return size of 0. - */ -static inline size_t utf32_codepoint_utf8_length(char32_t srcChar) -{ - // Figure out how many bytes the result will require. - if (srcChar < 0x00000080) { - return 1; - } else if (srcChar < 0x00000800) { - return 2; - } else if (srcChar < 0x00010000) { - if ((srcChar < kUnicodeSurrogateStart) || (srcChar > kUnicodeSurrogateEnd)) { - return 3; - } else { - // Surrogates are invalid UTF-32 characters. - return 0; - } - } - // Max code point for Unicode is 0x0010FFFF. - else if (srcChar <= kUnicodeMaxCodepoint) { - return 4; - } else { - // Invalid UTF-32 character. - return 0; - } -} - -// Write out the source character to . - -static inline void utf32_codepoint_to_utf8(uint8_t* dstP, char32_t srcChar, size_t bytes) -{ - dstP += bytes; - switch (bytes) - { /* note: everything falls through. */ - case 4: *--dstP = (uint8_t)((srcChar | kByteMark) & kByteMask); srcChar >>= 6; - case 3: *--dstP = (uint8_t)((srcChar | kByteMark) & kByteMask); srcChar >>= 6; - case 2: *--dstP = (uint8_t)((srcChar | kByteMark) & kByteMask); srcChar >>= 6; - case 1: *--dstP = (uint8_t)(srcChar | kFirstByteMark[bytes]); - } -} - -size_t strlen32(const char32_t *s) -{ - const char32_t *ss = s; - while ( *ss ) - ss++; - return ss-s; -} - -size_t strnlen32(const char32_t *s, size_t maxlen) -{ - const char32_t *ss = s; - while ((maxlen > 0) && *ss) { - ss++; - maxlen--; - } - return ss-s; -} - -static inline int32_t utf32_at_internal(const char* cur, size_t *num_read) -{ - const char first_char = *cur; - if ((first_char & 0x80) == 0) { // ASCII - *num_read = 1; - return *cur; - } - cur++; - char32_t mask, to_ignore_mask; - size_t num_to_read = 0; - char32_t utf32 = first_char; - for (num_to_read = 1, mask = 0x40, to_ignore_mask = 0xFFFFFF80; - (first_char & mask); - num_to_read++, to_ignore_mask |= mask, mask >>= 1) { - // 0x3F == 00111111 - utf32 = (utf32 << 6) + (*cur++ & 0x3F); - } - to_ignore_mask |= mask; - utf32 &= ~(to_ignore_mask << (6 * (num_to_read - 1))); - - *num_read = num_to_read; - return static_cast(utf32); -} - -int32_t utf32_from_utf8_at(const char *src, size_t src_len, size_t index, size_t *next_index) -{ - if (index >= src_len) { - return -1; - } - size_t dummy_index; - if (next_index == NULL) { - next_index = &dummy_index; - } - size_t num_read; - int32_t ret = utf32_at_internal(src + index, &num_read); - if (ret >= 0) { - *next_index = index + num_read; - } - - return ret; -} - -ssize_t utf32_to_utf8_length(const char32_t *src, size_t src_len) -{ - if (src == NULL || src_len == 0) { - return -1; - } - - size_t ret = 0; - const char32_t *end = src + src_len; - while (src < end) { - ret += utf32_codepoint_utf8_length(*src++); - } - return ret; -} - -void utf32_to_utf8(const char32_t* src, size_t src_len, char* dst) -{ - if (src == NULL || src_len == 0 || dst == NULL) { - return; - } - - const char32_t *cur_utf32 = src; - const char32_t *end_utf32 = src + src_len; - char *cur = dst; - while (cur_utf32 < end_utf32) { - size_t len = utf32_codepoint_utf8_length(*cur_utf32); - utf32_codepoint_to_utf8((uint8_t *)cur, *cur_utf32++, len); - cur += len; - } - *cur = '\0'; -} - -// -------------------------------------------------------------------------- -// UTF-16 -// -------------------------------------------------------------------------- - -int strcmp16(const char16_t *s1, const char16_t *s2) -{ - char16_t ch; - int d = 0; - - while ( 1 ) { - d = (int)(ch = *s1++) - (int)*s2++; - if ( d || !ch ) - break; - } - - return d; -} - -int strncmp16(const char16_t *s1, const char16_t *s2, size_t n) -{ - char16_t ch; - int d = 0; - - while ( n-- ) { - d = (int)(ch = *s1++) - (int)*s2++; - if ( d || !ch ) - break; - } - - return d; -} - -char16_t *strcpy16(char16_t *dst, const char16_t *src) -{ - char16_t *q = dst; - const char16_t *p = src; - char16_t ch; - - do { - *q++ = ch = *p++; - } while ( ch ); - - return dst; -} - -size_t strlen16(const char16_t *s) -{ - const char16_t *ss = s; - while ( *ss ) - ss++; - return ss-s; -} - - -char16_t *strncpy16(char16_t *dst, const char16_t *src, size_t n) -{ - char16_t *q = dst; - const char16_t *p = src; - char ch; - - while (n) { - n--; - *q++ = ch = *p++; - if ( !ch ) - break; - } - - *q = 0; - - return dst; -} - -size_t strnlen16(const char16_t *s, size_t maxlen) -{ - const char16_t *ss = s; - - /* Important: the maxlen test must precede the reference through ss; - since the byte beyond the maximum may segfault */ - while ((maxlen > 0) && *ss) { - ss++; - maxlen--; - } - return ss-s; -} - -int strzcmp16(const char16_t *s1, size_t n1, const char16_t *s2, size_t n2) -{ - const char16_t* e1 = s1+n1; - const char16_t* e2 = s2+n2; - - while (s1 < e1 && s2 < e2) { - const int d = (int)*s1++ - (int)*s2++; - if (d) { - return d; - } - } - - return n1 < n2 - ? (0 - (int)*s2) - : (n1 > n2 - ? ((int)*s1 - 0) - : 0); -} - -int strzcmp16_h_n(const char16_t *s1H, size_t n1, const char16_t *s2N, size_t n2) -{ - const char16_t* e1 = s1H+n1; - const char16_t* e2 = s2N+n2; - - while (s1H < e1 && s2N < e2) { - const char16_t c2 = ntohs(*s2N); - const int d = (int)*s1H++ - (int)c2; - s2N++; - if (d) { - return d; - } - } - - return n1 < n2 - ? (0 - (int)ntohs(*s2N)) - : (n1 > n2 - ? ((int)*s1H - 0) - : 0); -} - -void utf16_to_utf8(const char16_t* src, size_t src_len, char* dst) -{ - if (src == NULL || src_len == 0 || dst == NULL) { - return; - } - - const char16_t* cur_utf16 = src; - const char16_t* const end_utf16 = src + src_len; - char *cur = dst; - while (cur_utf16 < end_utf16) { - char32_t utf32; - // surrogate pairs - if ((*cur_utf16 & 0xFC00) == 0xD800) { - utf32 = (*cur_utf16++ - 0xD800) << 10; - utf32 |= *cur_utf16++ - 0xDC00; - utf32 += 0x10000; - } else { - utf32 = (char32_t) *cur_utf16++; - } - const size_t len = utf32_codepoint_utf8_length(utf32); - utf32_codepoint_to_utf8((uint8_t*)cur, utf32, len); - cur += len; - } - *cur = '\0'; -} - -// -------------------------------------------------------------------------- -// UTF-8 -// -------------------------------------------------------------------------- - -ssize_t utf8_length(const char *src) -{ - const char *cur = src; - size_t ret = 0; - while (*cur != '\0') { - const char first_char = *cur++; - if ((first_char & 0x80) == 0) { // ASCII - ret += 1; - continue; - } - // (UTF-8's character must not be like 10xxxxxx, - // but 110xxxxx, 1110xxxx, ... or 1111110x) - if ((first_char & 0x40) == 0) { - return -1; - } - - int32_t mask, to_ignore_mask; - size_t num_to_read = 0; - char32_t utf32 = 0; - for (num_to_read = 1, mask = 0x40, to_ignore_mask = 0x80; - num_to_read < 5 && (first_char & mask); - num_to_read++, to_ignore_mask |= mask, mask >>= 1) { - if ((*cur & 0xC0) != 0x80) { // must be 10xxxxxx - return -1; - } - // 0x3F == 00111111 - utf32 = (utf32 << 6) + (*cur++ & 0x3F); - } - // "first_char" must be (110xxxxx - 11110xxx) - if (num_to_read == 5) { - return -1; - } - to_ignore_mask |= mask; - utf32 |= ((~to_ignore_mask) & first_char) << (6 * (num_to_read - 1)); - if (utf32 > kUnicodeMaxCodepoint) { - return -1; - } - - ret += num_to_read; - } - return ret; -} - -ssize_t utf16_to_utf8_length(const char16_t *src, size_t src_len) -{ - if (src == NULL || src_len == 0) { - return -1; - } - - size_t ret = 0; - const char16_t* const end = src + src_len; - while (src < end) { - if ((*src & 0xFC00) == 0xD800 && (src + 1) < end - && (*++src & 0xFC00) == 0xDC00) { - // surrogate pairs are always 4 bytes. - ret += 4; - src++; - } else { - ret += utf32_codepoint_utf8_length((char32_t) *src++); - } - } - return ret; -} - -/** - * Returns 1-4 based on the number of leading bits. - * - * 1111 -> 4 - * 1110 -> 3 - * 110x -> 2 - * 10xx -> 1 - * 0xxx -> 1 - */ -static inline size_t utf8_codepoint_len(uint8_t ch) -{ - return ((0xe5000000 >> ((ch >> 3) & 0x1e)) & 3) + 1; -} - -static inline void utf8_shift_and_mask(uint32_t* codePoint, const uint8_t byte) -{ - *codePoint <<= 6; - *codePoint |= 0x3F & byte; -} - -size_t utf8_to_utf32_length(const char *src, size_t src_len) -{ - if (src == NULL || src_len == 0) { - return 0; - } - size_t ret = 0; - const char* cur; - const char* end; - size_t num_to_skip; - for (cur = src, end = src + src_len, num_to_skip = 1; - cur < end; - cur += num_to_skip, ret++) { - const char first_char = *cur; - num_to_skip = 1; - if ((first_char & 0x80) == 0) { // ASCII - continue; - } - int32_t mask; - - for (mask = 0x40; (first_char & mask); num_to_skip++, mask >>= 1) { - } - } - return ret; -} - -void utf8_to_utf32(const char* src, size_t src_len, char32_t* dst) -{ - if (src == NULL || src_len == 0 || dst == NULL) { - return; - } - - const char* cur = src; - const char* const end = src + src_len; - char32_t* cur_utf32 = dst; - while (cur < end) { - size_t num_read; - *cur_utf32++ = static_cast(utf32_at_internal(cur, &num_read)); - cur += num_read; - } - *cur_utf32 = 0; -} - -static inline uint32_t utf8_to_utf32_codepoint(const uint8_t *src, size_t length) -{ - uint32_t unicode; - - switch (length) - { - case 1: - return src[0]; - case 2: - unicode = src[0] & 0x1f; - utf8_shift_and_mask(&unicode, src[1]); - return unicode; - case 3: - unicode = src[0] & 0x0f; - utf8_shift_and_mask(&unicode, src[1]); - utf8_shift_and_mask(&unicode, src[2]); - return unicode; - case 4: - unicode = src[0] & 0x07; - utf8_shift_and_mask(&unicode, src[1]); - utf8_shift_and_mask(&unicode, src[2]); - utf8_shift_and_mask(&unicode, src[3]); - return unicode; - default: - return 0xffff; - } - - //printf("Char at %p: len=%d, utf-16=%p\n", src, length, (void*)result); -} - -ssize_t utf8_to_utf16_length(const uint8_t* u8str, size_t u8len) -{ - const uint8_t* const u8end = u8str + u8len; - const uint8_t* u8cur = u8str; - - /* Validate that the UTF-8 is the correct len */ - size_t u16measuredLen = 0; - while (u8cur < u8end) { - u16measuredLen++; - int u8charLen = utf8_codepoint_len(*u8cur); - uint32_t codepoint = utf8_to_utf32_codepoint(u8cur, u8charLen); - if (codepoint > 0xFFFF) u16measuredLen++; // this will be a surrogate pair in utf16 - u8cur += u8charLen; - } - - /** - * Make sure that we ended where we thought we would and the output UTF-16 - * will be exactly how long we were told it would be. - */ - if (u8cur != u8end) { - return -1; - } - - return u16measuredLen; -} - -char16_t* utf8_to_utf16_no_null_terminator(const uint8_t* u8str, size_t u8len, char16_t* u16str) -{ - const uint8_t* const u8end = u8str + u8len; - const uint8_t* u8cur = u8str; - char16_t* u16cur = u16str; - - while (u8cur < u8end) { - size_t u8len = utf8_codepoint_len(*u8cur); - uint32_t codepoint = utf8_to_utf32_codepoint(u8cur, u8len); - - // Convert the UTF32 codepoint to one or more UTF16 codepoints - if (codepoint <= 0xFFFF) { - // Single UTF16 character - *u16cur++ = (char16_t) codepoint; - } else { - // Multiple UTF16 characters with surrogates - codepoint = codepoint - 0x10000; - *u16cur++ = (char16_t) ((codepoint >> 10) + 0xD800); - *u16cur++ = (char16_t) ((codepoint & 0x3FF) + 0xDC00); - } - - u8cur += u8len; - } - return u16cur; -} - -void utf8_to_utf16(const uint8_t* u8str, size_t u8len, char16_t* u16str) { - char16_t* end = utf8_to_utf16_no_null_terminator(u8str, u8len, u16str); - *end = 0; -} - -} diff --git a/widget/gonk/libui/Unicode.h b/widget/gonk/libui/Unicode.h deleted file mode 100644 index 93a677df685c..000000000000 --- a/widget/gonk/libui/Unicode.h +++ /dev/null @@ -1,174 +0,0 @@ -/* - * Copyright (C) 2005 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ANDROID_UNICODE_H -#define ANDROID_UNICODE_H - -#define __STDC_LIMIT_MACROS 1 - -#include -#include - -extern "C" { - -// char32_t and char16_t are built-in types as of c++0x. -#if !defined(__GXX_EXPERIMENTAL_CXX0X__) && __cplusplus < 201103L -typedef uint32_t char32_t; -typedef uint16_t char16_t; -#endif - -// Standard string functions on char16_t strings. -int strcmp16(const char16_t *, const char16_t *); -int strncmp16(const char16_t *s1, const char16_t *s2, size_t n); -size_t strlen16(const char16_t *); -size_t strnlen16(const char16_t *, size_t); -char16_t *strcpy16(char16_t *, const char16_t *); -char16_t *strncpy16(char16_t *, const char16_t *, size_t); - -// Version of comparison that supports embedded nulls. -// This is different than strncmp() because we don't stop -// at a nul character and consider the strings to be different -// if the lengths are different (thus we need to supply the -// lengths of both strings). This can also be used when -// your string is not nul-terminated as it will have the -// equivalent result as strcmp16 (unlike strncmp16). -int strzcmp16(const char16_t *s1, size_t n1, const char16_t *s2, size_t n2); - -// Version of strzcmp16 for comparing strings in different endianness. -int strzcmp16_h_n(const char16_t *s1H, size_t n1, const char16_t *s2N, size_t n2); - -// Standard string functions on char32_t strings. -size_t strlen32(const char32_t *); -size_t strnlen32(const char32_t *, size_t); - -/** - * Measure the length of a UTF-32 string in UTF-8. If the string is invalid - * such as containing a surrogate character, -1 will be returned. - */ -ssize_t utf32_to_utf8_length(const char32_t *src, size_t src_len); - -/** - * Stores a UTF-8 string converted from "src" in "dst", if "dst_length" is not - * large enough to store the string, the part of the "src" string is stored - * into "dst" as much as possible. See the examples for more detail. - * Returns the size actually used for storing the string. - * dst" is not null-terminated when dst_len is fully used (like strncpy). - * - * Example 1 - * "src" == \u3042\u3044 (\xE3\x81\x82\xE3\x81\x84) - * "src_len" == 2 - * "dst_len" >= 7 - * -> - * Returned value == 6 - * "dst" becomes \xE3\x81\x82\xE3\x81\x84\0 - * (note that "dst" is null-terminated) - * - * Example 2 - * "src" == \u3042\u3044 (\xE3\x81\x82\xE3\x81\x84) - * "src_len" == 2 - * "dst_len" == 5 - * -> - * Returned value == 3 - * "dst" becomes \xE3\x81\x82\0 - * (note that "dst" is null-terminated, but \u3044 is not stored in "dst" - * since "dst" does not have enough size to store the character) - * - * Example 3 - * "src" == \u3042\u3044 (\xE3\x81\x82\xE3\x81\x84) - * "src_len" == 2 - * "dst_len" == 6 - * -> - * Returned value == 6 - * "dst" becomes \xE3\x81\x82\xE3\x81\x84 - * (note that "dst" is NOT null-terminated, like strncpy) - */ -void utf32_to_utf8(const char32_t* src, size_t src_len, char* dst); - -/** - * Returns the unicode value at "index". - * Returns -1 when the index is invalid (equals to or more than "src_len"). - * If returned value is positive, it is able to be converted to char32_t, which - * is unsigned. Then, if "next_index" is not NULL, the next index to be used is - * stored in "next_index". "next_index" can be NULL. - */ -int32_t utf32_from_utf8_at(const char *src, size_t src_len, size_t index, size_t *next_index); - - -/** - * Returns the UTF-8 length of UTF-16 string "src". - */ -ssize_t utf16_to_utf8_length(const char16_t *src, size_t src_len); - -/** - * Converts a UTF-16 string to UTF-8. The destination buffer must be large - * enough to fit the UTF-16 as measured by utf16_to_utf8_length with an added - * NULL terminator. - */ -void utf16_to_utf8(const char16_t* src, size_t src_len, char* dst); - -/** - * Returns the length of "src" when "src" is valid UTF-8 string. - * Returns 0 if src is NULL or 0-length string. Returns -1 when the source - * is an invalid string. - * - * This function should be used to determine whether "src" is valid UTF-8 - * characters with valid unicode codepoints. "src" must be null-terminated. - * - * If you are going to use other utf8_to_... functions defined in this header - * with string which may not be valid UTF-8 with valid codepoint (form 0 to - * 0x10FFFF), you should use this function before calling others, since the - * other functions do not check whether the string is valid UTF-8 or not. - * - * If you do not care whether "src" is valid UTF-8 or not, you should use - * strlen() as usual, which should be much faster. - */ -ssize_t utf8_length(const char *src); - -/** - * Measure the length of a UTF-32 string. - */ -size_t utf8_to_utf32_length(const char *src, size_t src_len); - -/** - * Stores a UTF-32 string converted from "src" in "dst". "dst" must be large - * enough to store the entire converted string as measured by - * utf8_to_utf32_length plus space for a NULL terminator. - */ -void utf8_to_utf32(const char* src, size_t src_len, char32_t* dst); - -/** - * Returns the UTF-16 length of UTF-8 string "src". - */ -ssize_t utf8_to_utf16_length(const uint8_t* src, size_t srcLen); - -/** - * Convert UTF-8 to UTF-16 including surrogate pairs. - * Returns a pointer to the end of the string (where a null terminator might go - * if you wanted to add one). - */ -char16_t* utf8_to_utf16_no_null_terminator(const uint8_t* src, size_t srcLen, char16_t* dst); - -/** - * Convert UTF-8 to UTF-16 including surrogate pairs. The destination buffer - * must be large enough to hold the result as measured by utf8_to_utf16_length - * plus an added NULL terminator. - */ -void utf8_to_utf16(const uint8_t* src, size_t srcLen, char16_t* dst); - -} - -#undef __STDC_LIMIT_MACROS -#endif diff --git a/widget/gonk/libui/VelocityControl.cpp b/widget/gonk/libui/VelocityControl.cpp new file mode 100644 index 000000000000..31365a220ac6 --- /dev/null +++ b/widget/gonk/libui/VelocityControl.cpp @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "VelocityControl" +//#define LOG_NDEBUG 0 + +// Log debug messages about acceleration. +#define DEBUG_ACCELERATION 0 + +#include +#include + +#include "VelocityControl.h" +#include +#include + +namespace android { + +// --- VelocityControl --- + +const nsecs_t VelocityControl::STOP_TIME; + +VelocityControl::VelocityControl() { + reset(); +} + +void VelocityControl::setParameters(const VelocityControlParameters& parameters) { + mParameters = parameters; + reset(); +} + +void VelocityControl::reset() { + mLastMovementTime = LLONG_MIN; + mRawPosition.x = 0; + mRawPosition.y = 0; + mVelocityTracker.clear(); +} + +void VelocityControl::move(nsecs_t eventTime, float* deltaX, float* deltaY) { + if ((deltaX && *deltaX) || (deltaY && *deltaY)) { + if (eventTime >= mLastMovementTime + STOP_TIME) { +#if DEBUG_ACCELERATION + ALOGD("VelocityControl: stopped, last movement was %0.3fms ago", + (eventTime - mLastMovementTime) * 0.000001f); +#endif + reset(); + } + + mLastMovementTime = eventTime; + if (deltaX) { + mRawPosition.x += *deltaX; + } + if (deltaY) { + mRawPosition.y += *deltaY; + } + mVelocityTracker.addMovement(eventTime, BitSet32(BitSet32::valueForBit(0)), &mRawPosition); + + float vx, vy; + float scale = mParameters.scale; + if (mVelocityTracker.getVelocity(0, &vx, &vy)) { + float speed = hypotf(vx, vy) * scale; + if (speed >= mParameters.highThreshold) { + // Apply full acceleration above the high speed threshold. + scale *= mParameters.acceleration; + } else if (speed > mParameters.lowThreshold) { + // Linearly interpolate the acceleration to apply between the low and high + // speed thresholds. + scale *= 1 + (speed - mParameters.lowThreshold) + / (mParameters.highThreshold - mParameters.lowThreshold) + * (mParameters.acceleration - 1); + } + +#if DEBUG_ACCELERATION + ALOGD("VelocityControl(%0.3f, %0.3f, %0.3f, %0.3f): " + "vx=%0.3f, vy=%0.3f, speed=%0.3f, accel=%0.3f", + mParameters.scale, mParameters.lowThreshold, mParameters.highThreshold, + mParameters.acceleration, + vx, vy, speed, scale / mParameters.scale); +#endif + } else { +#if DEBUG_ACCELERATION + ALOGD("VelocityControl(%0.3f, %0.3f, %0.3f, %0.3f): unknown velocity", + mParameters.scale, mParameters.lowThreshold, mParameters.highThreshold, + mParameters.acceleration); +#endif + } + + if (deltaX) { + *deltaX *= scale; + } + if (deltaY) { + *deltaY *= scale; + } + } +} + +} // namespace android diff --git a/widget/gonk/libui/VelocityControl.h b/widget/gonk/libui/VelocityControl.h new file mode 100644 index 000000000000..8a2c695d6fcd --- /dev/null +++ b/widget/gonk/libui/VelocityControl.h @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _ANDROIDFW_VELOCITY_CONTROL_H +#define _ANDROIDFW_VELOCITY_CONTROL_H + +#include "Input.h" +#include "VelocityTracker.h" +#include + +namespace android { + +/* + * Specifies parameters that govern pointer or wheel acceleration. + */ +struct VelocityControlParameters { + // A scale factor that is multiplied with the raw velocity deltas + // prior to applying any other velocity control factors. The scale + // factor should be used to adapt the input device resolution + // (eg. counts per inch) to the output device resolution (eg. pixels per inch). + // + // Must be a positive value. + // Default is 1.0 (no scaling). + float scale; + + // The scaled speed at which acceleration begins to be applied. + // This value establishes the upper bound of a low speed regime for + // small precise motions that are performed without any acceleration. + // + // Must be a non-negative value. + // Default is 0.0 (no low threshold). + float lowThreshold; + + // The scaled speed at which maximum acceleration is applied. + // The difference between highThreshold and lowThreshold controls + // the range of speeds over which the acceleration factor is interpolated. + // The wider the range, the smoother the acceleration. + // + // Must be a non-negative value greater than or equal to lowThreshold. + // Default is 0.0 (no high threshold). + float highThreshold; + + // The acceleration factor. + // When the speed is above the low speed threshold, the velocity will scaled + // by an interpolated value between 1.0 and this amount. + // + // Must be a positive greater than or equal to 1.0. + // Default is 1.0 (no acceleration). + float acceleration; + + VelocityControlParameters() : + scale(1.0f), lowThreshold(0.0f), highThreshold(0.0f), acceleration(1.0f) { + } + + VelocityControlParameters(float scale, float lowThreshold, + float highThreshold, float acceleration) : + scale(scale), lowThreshold(lowThreshold), + highThreshold(highThreshold), acceleration(acceleration) { + } +}; + +/* + * Implements mouse pointer and wheel speed control and acceleration. + */ +class VelocityControl { +public: + VelocityControl(); + + /* Sets the various parameters. */ + void setParameters(const VelocityControlParameters& parameters); + + /* Resets the current movement counters to zero. + * This has the effect of nullifying any acceleration. */ + void reset(); + + /* Translates a raw movement delta into an appropriately + * scaled / accelerated delta based on the current velocity. */ + void move(nsecs_t eventTime, float* deltaX, float* deltaY); + +private: + // If no movements are received within this amount of time, + // we assume the movement has stopped and reset the movement counters. + static const nsecs_t STOP_TIME = 500 * 1000000; // 500 ms + + VelocityControlParameters mParameters; + + nsecs_t mLastMovementTime; + VelocityTracker::Position mRawPosition; + VelocityTracker mVelocityTracker; +}; + +} // namespace android + +#endif // _ANDROIDFW_VELOCITY_CONTROL_H diff --git a/widget/gonk/libui/VelocityTracker.cpp b/widget/gonk/libui/VelocityTracker.cpp new file mode 100644 index 000000000000..11a8bf7fc056 --- /dev/null +++ b/widget/gonk/libui/VelocityTracker.cpp @@ -0,0 +1,929 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "VelocityTracker" +//#define LOG_NDEBUG 0 +#include "cutils_log.h" + +// Log debug messages about velocity tracking. +#define DEBUG_VELOCITY 0 + +// Log debug messages about the progress of the algorithm itself. +#define DEBUG_STRATEGY 0 + +#include +#include + +#include "VelocityTracker.h" +#include +#include +#include + +#include + +namespace android { + +// Nanoseconds per milliseconds. +static const nsecs_t NANOS_PER_MS = 1000000; + +// Threshold for determining that a pointer has stopped moving. +// Some input devices do not send ACTION_MOVE events in the case where a pointer has +// stopped. We need to detect this case so that we can accurately predict the +// velocity after the pointer starts moving again. +static const nsecs_t ASSUME_POINTER_STOPPED_TIME = 40 * NANOS_PER_MS; + + +static float vectorDot(const float* a, const float* b, uint32_t m) { + float r = 0; + while (m--) { + r += *(a++) * *(b++); + } + return r; +} + +static float vectorNorm(const float* a, uint32_t m) { + float r = 0; + while (m--) { + float t = *(a++); + r += t * t; + } + return sqrtf(r); +} + +#if DEBUG_STRATEGY || DEBUG_VELOCITY +static String8 vectorToString(const float* a, uint32_t m) { + String8 str; + str.append("["); + while (m--) { + str.appendFormat(" %f", *(a++)); + if (m) { + str.append(","); + } + } + str.append(" ]"); + return str; +} + +static String8 matrixToString(const float* a, uint32_t m, uint32_t n, bool rowMajor) { + String8 str; + str.append("["); + for (size_t i = 0; i < m; i++) { + if (i) { + str.append(","); + } + str.append(" ["); + for (size_t j = 0; j < n; j++) { + if (j) { + str.append(","); + } + str.appendFormat(" %f", a[rowMajor ? i * n + j : j * m + i]); + } + str.append(" ]"); + } + str.append(" ]"); + return str; +} +#endif + + +// --- VelocityTracker --- + +// The default velocity tracker strategy. +// Although other strategies are available for testing and comparison purposes, +// this is the strategy that applications will actually use. Be very careful +// when adjusting the default strategy because it can dramatically affect +// (often in a bad way) the user experience. +const char* VelocityTracker::DEFAULT_STRATEGY = "lsq2"; + +VelocityTracker::VelocityTracker(const char* strategy) : + mLastEventTime(0), mCurrentPointerIdBits(0), mActivePointerId(-1) { + char value[PROPERTY_VALUE_MAX]; + + // Allow the default strategy to be overridden using a system property for debugging. + if (!strategy) { + int length = property_get("debug.velocitytracker.strategy", value, NULL); + if (length > 0) { + strategy = value; + } else { + strategy = DEFAULT_STRATEGY; + } + } + + // Configure the strategy. + if (!configureStrategy(strategy)) { + ALOGD("Unrecognized velocity tracker strategy name '%s'.", strategy); + if (!configureStrategy(DEFAULT_STRATEGY)) { + LOG_ALWAYS_FATAL("Could not create the default velocity tracker strategy '%s'!", + strategy); + } + } +} + +VelocityTracker::~VelocityTracker() { + delete mStrategy; +} + +bool VelocityTracker::configureStrategy(const char* strategy) { + mStrategy = createStrategy(strategy); + return mStrategy != NULL; +} + +VelocityTrackerStrategy* VelocityTracker::createStrategy(const char* strategy) { + if (!strcmp("lsq1", strategy)) { + // 1st order least squares. Quality: POOR. + // Frequently underfits the touch data especially when the finger accelerates + // or changes direction. Often underestimates velocity. The direction + // is overly influenced by historical touch points. + return new LeastSquaresVelocityTrackerStrategy(1); + } + if (!strcmp("lsq2", strategy)) { + // 2nd order least squares. Quality: VERY GOOD. + // Pretty much ideal, but can be confused by certain kinds of touch data, + // particularly if the panel has a tendency to generate delayed, + // duplicate or jittery touch coordinates when the finger is released. + return new LeastSquaresVelocityTrackerStrategy(2); + } + if (!strcmp("lsq3", strategy)) { + // 3rd order least squares. Quality: UNUSABLE. + // Frequently overfits the touch data yielding wildly divergent estimates + // of the velocity when the finger is released. + return new LeastSquaresVelocityTrackerStrategy(3); + } + if (!strcmp("wlsq2-delta", strategy)) { + // 2nd order weighted least squares, delta weighting. Quality: EXPERIMENTAL + return new LeastSquaresVelocityTrackerStrategy(2, + LeastSquaresVelocityTrackerStrategy::WEIGHTING_DELTA); + } + if (!strcmp("wlsq2-central", strategy)) { + // 2nd order weighted least squares, central weighting. Quality: EXPERIMENTAL + return new LeastSquaresVelocityTrackerStrategy(2, + LeastSquaresVelocityTrackerStrategy::WEIGHTING_CENTRAL); + } + if (!strcmp("wlsq2-recent", strategy)) { + // 2nd order weighted least squares, recent weighting. Quality: EXPERIMENTAL + return new LeastSquaresVelocityTrackerStrategy(2, + LeastSquaresVelocityTrackerStrategy::WEIGHTING_RECENT); + } + if (!strcmp("int1", strategy)) { + // 1st order integrating filter. Quality: GOOD. + // Not as good as 'lsq2' because it cannot estimate acceleration but it is + // more tolerant of errors. Like 'lsq1', this strategy tends to underestimate + // the velocity of a fling but this strategy tends to respond to changes in + // direction more quickly and accurately. + return new IntegratingVelocityTrackerStrategy(1); + } + if (!strcmp("int2", strategy)) { + // 2nd order integrating filter. Quality: EXPERIMENTAL. + // For comparison purposes only. Unlike 'int1' this strategy can compensate + // for acceleration but it typically overestimates the effect. + return new IntegratingVelocityTrackerStrategy(2); + } + if (!strcmp("legacy", strategy)) { + // Legacy velocity tracker algorithm. Quality: POOR. + // For comparison purposes only. This algorithm is strongly influenced by + // old data points, consistently underestimates velocity and takes a very long + // time to adjust to changes in direction. + return new LegacyVelocityTrackerStrategy(); + } + return NULL; +} + +void VelocityTracker::clear() { + mCurrentPointerIdBits.clear(); + mActivePointerId = -1; + + mStrategy->clear(); +} + +void VelocityTracker::clearPointers(BitSet32 idBits) { + BitSet32 remainingIdBits(mCurrentPointerIdBits.value & ~idBits.value); + mCurrentPointerIdBits = remainingIdBits; + + if (mActivePointerId >= 0 && idBits.hasBit(mActivePointerId)) { + mActivePointerId = !remainingIdBits.isEmpty() ? remainingIdBits.firstMarkedBit() : -1; + } + + mStrategy->clearPointers(idBits); +} + +void VelocityTracker::addMovement(nsecs_t eventTime, BitSet32 idBits, const Position* positions) { + while (idBits.count() > MAX_POINTERS) { + idBits.clearLastMarkedBit(); + } + + if ((mCurrentPointerIdBits.value & idBits.value) + && eventTime >= mLastEventTime + ASSUME_POINTER_STOPPED_TIME) { +#if DEBUG_VELOCITY + ALOGD("VelocityTracker: stopped for %0.3f ms, clearing state.", + (eventTime - mLastEventTime) * 0.000001f); +#endif + // We have not received any movements for too long. Assume that all pointers + // have stopped. + mStrategy->clear(); + } + mLastEventTime = eventTime; + + mCurrentPointerIdBits = idBits; + if (mActivePointerId < 0 || !idBits.hasBit(mActivePointerId)) { + mActivePointerId = idBits.isEmpty() ? -1 : idBits.firstMarkedBit(); + } + + mStrategy->addMovement(eventTime, idBits, positions); + +#if DEBUG_VELOCITY + ALOGD("VelocityTracker: addMovement eventTime=%lld, idBits=0x%08x, activePointerId=%d", + eventTime, idBits.value, mActivePointerId); + for (BitSet32 iterBits(idBits); !iterBits.isEmpty(); ) { + uint32_t id = iterBits.firstMarkedBit(); + uint32_t index = idBits.getIndexOfBit(id); + iterBits.clearBit(id); + Estimator estimator; + getEstimator(id, &estimator); + ALOGD(" %d: position (%0.3f, %0.3f), " + "estimator (degree=%d, xCoeff=%s, yCoeff=%s, confidence=%f)", + id, positions[index].x, positions[index].y, + int(estimator.degree), + vectorToString(estimator.xCoeff, estimator.degree + 1).string(), + vectorToString(estimator.yCoeff, estimator.degree + 1).string(), + estimator.confidence); + } +#endif +} + +void VelocityTracker::addMovement(const MotionEvent* event) { + int32_t actionMasked = event->getActionMasked(); + + switch (actionMasked) { + case AMOTION_EVENT_ACTION_DOWN: + case AMOTION_EVENT_ACTION_HOVER_ENTER: + // Clear all pointers on down before adding the new movement. + clear(); + break; + case AMOTION_EVENT_ACTION_POINTER_DOWN: { + // Start a new movement trace for a pointer that just went down. + // We do this on down instead of on up because the client may want to query the + // final velocity for a pointer that just went up. + BitSet32 downIdBits; + downIdBits.markBit(event->getPointerId(event->getActionIndex())); + clearPointers(downIdBits); + break; + } + case AMOTION_EVENT_ACTION_MOVE: + case AMOTION_EVENT_ACTION_HOVER_MOVE: + break; + default: + // Ignore all other actions because they do not convey any new information about + // pointer movement. We also want to preserve the last known velocity of the pointers. + // Note that ACTION_UP and ACTION_POINTER_UP always report the last known position + // of the pointers that went up. ACTION_POINTER_UP does include the new position of + // pointers that remained down but we will also receive an ACTION_MOVE with this + // information if any of them actually moved. Since we don't know how many pointers + // will be going up at once it makes sense to just wait for the following ACTION_MOVE + // before adding the movement. + return; + } + + size_t pointerCount = event->getPointerCount(); + if (pointerCount > MAX_POINTERS) { + pointerCount = MAX_POINTERS; + } + + BitSet32 idBits; + for (size_t i = 0; i < pointerCount; i++) { + idBits.markBit(event->getPointerId(i)); + } + + uint32_t pointerIndex[MAX_POINTERS]; + for (size_t i = 0; i < pointerCount; i++) { + pointerIndex[i] = idBits.getIndexOfBit(event->getPointerId(i)); + } + + nsecs_t eventTime; + Position positions[pointerCount]; + + size_t historySize = event->getHistorySize(); + for (size_t h = 0; h < historySize; h++) { + eventTime = event->getHistoricalEventTime(h); + for (size_t i = 0; i < pointerCount; i++) { + uint32_t index = pointerIndex[i]; + positions[index].x = event->getHistoricalX(i, h); + positions[index].y = event->getHistoricalY(i, h); + } + addMovement(eventTime, idBits, positions); + } + + eventTime = event->getEventTime(); + for (size_t i = 0; i < pointerCount; i++) { + uint32_t index = pointerIndex[i]; + positions[index].x = event->getX(i); + positions[index].y = event->getY(i); + } + addMovement(eventTime, idBits, positions); +} + +bool VelocityTracker::getVelocity(uint32_t id, float* outVx, float* outVy) const { + Estimator estimator; + if (getEstimator(id, &estimator) && estimator.degree >= 1) { + *outVx = estimator.xCoeff[1]; + *outVy = estimator.yCoeff[1]; + return true; + } + *outVx = 0; + *outVy = 0; + return false; +} + +bool VelocityTracker::getEstimator(uint32_t id, Estimator* outEstimator) const { + return mStrategy->getEstimator(id, outEstimator); +} + + +// --- LeastSquaresVelocityTrackerStrategy --- + +const nsecs_t LeastSquaresVelocityTrackerStrategy::HORIZON; +const uint32_t LeastSquaresVelocityTrackerStrategy::HISTORY_SIZE; + +LeastSquaresVelocityTrackerStrategy::LeastSquaresVelocityTrackerStrategy( + uint32_t degree, Weighting weighting) : + mDegree(degree), mWeighting(weighting) { + clear(); +} + +LeastSquaresVelocityTrackerStrategy::~LeastSquaresVelocityTrackerStrategy() { +} + +void LeastSquaresVelocityTrackerStrategy::clear() { + mIndex = 0; + mMovements[0].idBits.clear(); +} + +void LeastSquaresVelocityTrackerStrategy::clearPointers(BitSet32 idBits) { + BitSet32 remainingIdBits(mMovements[mIndex].idBits.value & ~idBits.value); + mMovements[mIndex].idBits = remainingIdBits; +} + +void LeastSquaresVelocityTrackerStrategy::addMovement(nsecs_t eventTime, BitSet32 idBits, + const VelocityTracker::Position* positions) { + if (++mIndex == HISTORY_SIZE) { + mIndex = 0; + } + + Movement& movement = mMovements[mIndex]; + movement.eventTime = eventTime; + movement.idBits = idBits; + uint32_t count = idBits.count(); + for (uint32_t i = 0; i < count; i++) { + movement.positions[i] = positions[i]; + } +} + +/** + * Solves a linear least squares problem to obtain a N degree polynomial that fits + * the specified input data as nearly as possible. + * + * Returns true if a solution is found, false otherwise. + * + * The input consists of two vectors of data points X and Y with indices 0..m-1 + * along with a weight vector W of the same size. + * + * The output is a vector B with indices 0..n that describes a polynomial + * that fits the data, such the sum of W[i] * W[i] * abs(Y[i] - (B[0] + B[1] X[i] + * + B[2] X[i]^2 ... B[n] X[i]^n)) for all i between 0 and m-1 is minimized. + * + * Accordingly, the weight vector W should be initialized by the caller with the + * reciprocal square root of the variance of the error in each input data point. + * In other words, an ideal choice for W would be W[i] = 1 / var(Y[i]) = 1 / stddev(Y[i]). + * The weights express the relative importance of each data point. If the weights are + * all 1, then the data points are considered to be of equal importance when fitting + * the polynomial. It is a good idea to choose weights that diminish the importance + * of data points that may have higher than usual error margins. + * + * Errors among data points are assumed to be independent. W is represented here + * as a vector although in the literature it is typically taken to be a diagonal matrix. + * + * That is to say, the function that generated the input data can be approximated + * by y(x) ~= B[0] + B[1] x + B[2] x^2 + ... + B[n] x^n. + * + * The coefficient of determination (R^2) is also returned to describe the goodness + * of fit of the model for the given data. It is a value between 0 and 1, where 1 + * indicates perfect correspondence. + * + * This function first expands the X vector to a m by n matrix A such that + * A[i][0] = 1, A[i][1] = X[i], A[i][2] = X[i]^2, ..., A[i][n] = X[i]^n, then + * multiplies it by w[i]./ + * + * Then it calculates the QR decomposition of A yielding an m by m orthonormal matrix Q + * and an m by n upper triangular matrix R. Because R is upper triangular (lower + * part is all zeroes), we can simplify the decomposition into an m by n matrix + * Q1 and a n by n matrix R1 such that A = Q1 R1. + * + * Finally we solve the system of linear equations given by R1 B = (Qtranspose W Y) + * to find B. + * + * For efficiency, we lay out A and Q column-wise in memory because we frequently + * operate on the column vectors. Conversely, we lay out R row-wise. + * + * http://en.wikipedia.org/wiki/Numerical_methods_for_linear_least_squares + * http://en.wikipedia.org/wiki/Gram-Schmidt + */ +static bool solveLeastSquares(const float* x, const float* y, + const float* w, uint32_t m, uint32_t n, float* outB, float* outDet) { +#if DEBUG_STRATEGY + ALOGD("solveLeastSquares: m=%d, n=%d, x=%s, y=%s, w=%s", int(m), int(n), + vectorToString(x, m).string(), vectorToString(y, m).string(), + vectorToString(w, m).string()); +#endif + + // Expand the X vector to a matrix A, pre-multiplied by the weights. + float a[n][m]; // column-major order + for (uint32_t h = 0; h < m; h++) { + a[0][h] = w[h]; + for (uint32_t i = 1; i < n; i++) { + a[i][h] = a[i - 1][h] * x[h]; + } + } +#if DEBUG_STRATEGY + ALOGD(" - a=%s", matrixToString(&a[0][0], m, n, false /*rowMajor*/).string()); +#endif + + // Apply the Gram-Schmidt process to A to obtain its QR decomposition. + float q[n][m]; // orthonormal basis, column-major order + float r[n][n]; // upper triangular matrix, row-major order + for (uint32_t j = 0; j < n; j++) { + for (uint32_t h = 0; h < m; h++) { + q[j][h] = a[j][h]; + } + for (uint32_t i = 0; i < j; i++) { + float dot = vectorDot(&q[j][0], &q[i][0], m); + for (uint32_t h = 0; h < m; h++) { + q[j][h] -= dot * q[i][h]; + } + } + + float norm = vectorNorm(&q[j][0], m); + if (norm < 0.000001f) { + // vectors are linearly dependent or zero so no solution +#if DEBUG_STRATEGY + ALOGD(" - no solution, norm=%f", norm); +#endif + return false; + } + + float invNorm = 1.0f / norm; + for (uint32_t h = 0; h < m; h++) { + q[j][h] *= invNorm; + } + for (uint32_t i = 0; i < n; i++) { + r[j][i] = i < j ? 0 : vectorDot(&q[j][0], &a[i][0], m); + } + } +#if DEBUG_STRATEGY + ALOGD(" - q=%s", matrixToString(&q[0][0], m, n, false /*rowMajor*/).string()); + ALOGD(" - r=%s", matrixToString(&r[0][0], n, n, true /*rowMajor*/).string()); + + // calculate QR, if we factored A correctly then QR should equal A + float qr[n][m]; + for (uint32_t h = 0; h < m; h++) { + for (uint32_t i = 0; i < n; i++) { + qr[i][h] = 0; + for (uint32_t j = 0; j < n; j++) { + qr[i][h] += q[j][h] * r[j][i]; + } + } + } + ALOGD(" - qr=%s", matrixToString(&qr[0][0], m, n, false /*rowMajor*/).string()); +#endif + + // Solve R B = Qt W Y to find B. This is easy because R is upper triangular. + // We just work from bottom-right to top-left calculating B's coefficients. + float wy[m]; + for (uint32_t h = 0; h < m; h++) { + wy[h] = y[h] * w[h]; + } + for (uint32_t i = n; i-- != 0; ) { + outB[i] = vectorDot(&q[i][0], wy, m); + for (uint32_t j = n - 1; j > i; j--) { + outB[i] -= r[i][j] * outB[j]; + } + outB[i] /= r[i][i]; + } +#if DEBUG_STRATEGY + ALOGD(" - b=%s", vectorToString(outB, n).string()); +#endif + + // Calculate the coefficient of determination as 1 - (SSerr / SStot) where + // SSerr is the residual sum of squares (variance of the error), + // and SStot is the total sum of squares (variance of the data) where each + // has been weighted. + float ymean = 0; + for (uint32_t h = 0; h < m; h++) { + ymean += y[h]; + } + ymean /= m; + + float sserr = 0; + float sstot = 0; + for (uint32_t h = 0; h < m; h++) { + float err = y[h] - outB[0]; + float term = 1; + for (uint32_t i = 1; i < n; i++) { + term *= x[h]; + err -= term * outB[i]; + } + sserr += w[h] * w[h] * err * err; + float var = y[h] - ymean; + sstot += w[h] * w[h] * var * var; + } + *outDet = sstot > 0.000001f ? 1.0f - (sserr / sstot) : 1; +#if DEBUG_STRATEGY + ALOGD(" - sserr=%f", sserr); + ALOGD(" - sstot=%f", sstot); + ALOGD(" - det=%f", *outDet); +#endif + return true; +} + +bool LeastSquaresVelocityTrackerStrategy::getEstimator(uint32_t id, + VelocityTracker::Estimator* outEstimator) const { + outEstimator->clear(); + + // Iterate over movement samples in reverse time order and collect samples. + float x[HISTORY_SIZE]; + float y[HISTORY_SIZE]; + float w[HISTORY_SIZE]; + float time[HISTORY_SIZE]; + uint32_t m = 0; + uint32_t index = mIndex; + const Movement& newestMovement = mMovements[mIndex]; + do { + const Movement& movement = mMovements[index]; + if (!movement.idBits.hasBit(id)) { + break; + } + + nsecs_t age = newestMovement.eventTime - movement.eventTime; + if (age > HORIZON) { + break; + } + + const VelocityTracker::Position& position = movement.getPosition(id); + x[m] = position.x; + y[m] = position.y; + w[m] = chooseWeight(index); + time[m] = -age * 0.000000001f; + index = (index == 0 ? HISTORY_SIZE : index) - 1; + } while (++m < HISTORY_SIZE); + + if (m == 0) { + return false; // no data + } + + // Calculate a least squares polynomial fit. + uint32_t degree = mDegree; + if (degree > m - 1) { + degree = m - 1; + } + if (degree >= 1) { + float xdet, ydet; + uint32_t n = degree + 1; + if (solveLeastSquares(time, x, w, m, n, outEstimator->xCoeff, &xdet) + && solveLeastSquares(time, y, w, m, n, outEstimator->yCoeff, &ydet)) { + outEstimator->time = newestMovement.eventTime; + outEstimator->degree = degree; + outEstimator->confidence = xdet * ydet; +#if DEBUG_STRATEGY + ALOGD("estimate: degree=%d, xCoeff=%s, yCoeff=%s, confidence=%f", + int(outEstimator->degree), + vectorToString(outEstimator->xCoeff, n).string(), + vectorToString(outEstimator->yCoeff, n).string(), + outEstimator->confidence); +#endif + return true; + } + } + + // No velocity data available for this pointer, but we do have its current position. + outEstimator->xCoeff[0] = x[0]; + outEstimator->yCoeff[0] = y[0]; + outEstimator->time = newestMovement.eventTime; + outEstimator->degree = 0; + outEstimator->confidence = 1; + return true; +} + +float LeastSquaresVelocityTrackerStrategy::chooseWeight(uint32_t index) const { + switch (mWeighting) { + case WEIGHTING_DELTA: { + // Weight points based on how much time elapsed between them and the next + // point so that points that "cover" a shorter time span are weighed less. + // delta 0ms: 0.5 + // delta 10ms: 1.0 + if (index == mIndex) { + return 1.0f; + } + uint32_t nextIndex = (index + 1) % HISTORY_SIZE; + float deltaMillis = (mMovements[nextIndex].eventTime- mMovements[index].eventTime) + * 0.000001f; + if (deltaMillis < 0) { + return 0.5f; + } + if (deltaMillis < 10) { + return 0.5f + deltaMillis * 0.05; + } + return 1.0f; + } + + case WEIGHTING_CENTRAL: { + // Weight points based on their age, weighing very recent and very old points less. + // age 0ms: 0.5 + // age 10ms: 1.0 + // age 50ms: 1.0 + // age 60ms: 0.5 + float ageMillis = (mMovements[mIndex].eventTime - mMovements[index].eventTime) + * 0.000001f; + if (ageMillis < 0) { + return 0.5f; + } + if (ageMillis < 10) { + return 0.5f + ageMillis * 0.05; + } + if (ageMillis < 50) { + return 1.0f; + } + if (ageMillis < 60) { + return 0.5f + (60 - ageMillis) * 0.05; + } + return 0.5f; + } + + case WEIGHTING_RECENT: { + // Weight points based on their age, weighing older points less. + // age 0ms: 1.0 + // age 50ms: 1.0 + // age 100ms: 0.5 + float ageMillis = (mMovements[mIndex].eventTime - mMovements[index].eventTime) + * 0.000001f; + if (ageMillis < 50) { + return 1.0f; + } + if (ageMillis < 100) { + return 0.5f + (100 - ageMillis) * 0.01f; + } + return 0.5f; + } + + case WEIGHTING_NONE: + default: + return 1.0f; + } +} + + +// --- IntegratingVelocityTrackerStrategy --- + +IntegratingVelocityTrackerStrategy::IntegratingVelocityTrackerStrategy(uint32_t degree) : + mDegree(degree) { +} + +IntegratingVelocityTrackerStrategy::~IntegratingVelocityTrackerStrategy() { +} + +void IntegratingVelocityTrackerStrategy::clear() { + mPointerIdBits.clear(); +} + +void IntegratingVelocityTrackerStrategy::clearPointers(BitSet32 idBits) { + mPointerIdBits.value &= ~idBits.value; +} + +void IntegratingVelocityTrackerStrategy::addMovement(nsecs_t eventTime, BitSet32 idBits, + const VelocityTracker::Position* positions) { + uint32_t index = 0; + for (BitSet32 iterIdBits(idBits); !iterIdBits.isEmpty();) { + uint32_t id = iterIdBits.clearFirstMarkedBit(); + State& state = mPointerState[id]; + const VelocityTracker::Position& position = positions[index++]; + if (mPointerIdBits.hasBit(id)) { + updateState(state, eventTime, position.x, position.y); + } else { + initState(state, eventTime, position.x, position.y); + } + } + + mPointerIdBits = idBits; +} + +bool IntegratingVelocityTrackerStrategy::getEstimator(uint32_t id, + VelocityTracker::Estimator* outEstimator) const { + outEstimator->clear(); + + if (mPointerIdBits.hasBit(id)) { + const State& state = mPointerState[id]; + populateEstimator(state, outEstimator); + return true; + } + + return false; +} + +void IntegratingVelocityTrackerStrategy::initState(State& state, + nsecs_t eventTime, float xpos, float ypos) const { + state.updateTime = eventTime; + state.degree = 0; + + state.xpos = xpos; + state.xvel = 0; + state.xaccel = 0; + state.ypos = ypos; + state.yvel = 0; + state.yaccel = 0; +} + +void IntegratingVelocityTrackerStrategy::updateState(State& state, + nsecs_t eventTime, float xpos, float ypos) const { + const nsecs_t MIN_TIME_DELTA = 2 * NANOS_PER_MS; + const float FILTER_TIME_CONSTANT = 0.010f; // 10 milliseconds + + if (eventTime <= state.updateTime + MIN_TIME_DELTA) { + return; + } + + float dt = (eventTime - state.updateTime) * 0.000000001f; + state.updateTime = eventTime; + + float xvel = (xpos - state.xpos) / dt; + float yvel = (ypos - state.ypos) / dt; + if (state.degree == 0) { + state.xvel = xvel; + state.yvel = yvel; + state.degree = 1; + } else { + float alpha = dt / (FILTER_TIME_CONSTANT + dt); + if (mDegree == 1) { + state.xvel += (xvel - state.xvel) * alpha; + state.yvel += (yvel - state.yvel) * alpha; + } else { + float xaccel = (xvel - state.xvel) / dt; + float yaccel = (yvel - state.yvel) / dt; + if (state.degree == 1) { + state.xaccel = xaccel; + state.yaccel = yaccel; + state.degree = 2; + } else { + state.xaccel += (xaccel - state.xaccel) * alpha; + state.yaccel += (yaccel - state.yaccel) * alpha; + } + state.xvel += (state.xaccel * dt) * alpha; + state.yvel += (state.yaccel * dt) * alpha; + } + } + state.xpos = xpos; + state.ypos = ypos; +} + +void IntegratingVelocityTrackerStrategy::populateEstimator(const State& state, + VelocityTracker::Estimator* outEstimator) const { + outEstimator->time = state.updateTime; + outEstimator->confidence = 1.0f; + outEstimator->degree = state.degree; + outEstimator->xCoeff[0] = state.xpos; + outEstimator->xCoeff[1] = state.xvel; + outEstimator->xCoeff[2] = state.xaccel / 2; + outEstimator->yCoeff[0] = state.ypos; + outEstimator->yCoeff[1] = state.yvel; + outEstimator->yCoeff[2] = state.yaccel / 2; +} + + +// --- LegacyVelocityTrackerStrategy --- + +const nsecs_t LegacyVelocityTrackerStrategy::HORIZON; +const uint32_t LegacyVelocityTrackerStrategy::HISTORY_SIZE; +const nsecs_t LegacyVelocityTrackerStrategy::MIN_DURATION; + +LegacyVelocityTrackerStrategy::LegacyVelocityTrackerStrategy() { + clear(); +} + +LegacyVelocityTrackerStrategy::~LegacyVelocityTrackerStrategy() { +} + +void LegacyVelocityTrackerStrategy::clear() { + mIndex = 0; + mMovements[0].idBits.clear(); +} + +void LegacyVelocityTrackerStrategy::clearPointers(BitSet32 idBits) { + BitSet32 remainingIdBits(mMovements[mIndex].idBits.value & ~idBits.value); + mMovements[mIndex].idBits = remainingIdBits; +} + +void LegacyVelocityTrackerStrategy::addMovement(nsecs_t eventTime, BitSet32 idBits, + const VelocityTracker::Position* positions) { + if (++mIndex == HISTORY_SIZE) { + mIndex = 0; + } + + Movement& movement = mMovements[mIndex]; + movement.eventTime = eventTime; + movement.idBits = idBits; + uint32_t count = idBits.count(); + for (uint32_t i = 0; i < count; i++) { + movement.positions[i] = positions[i]; + } +} + +bool LegacyVelocityTrackerStrategy::getEstimator(uint32_t id, + VelocityTracker::Estimator* outEstimator) const { + outEstimator->clear(); + + const Movement& newestMovement = mMovements[mIndex]; + if (!newestMovement.idBits.hasBit(id)) { + return false; // no data + } + + // Find the oldest sample that contains the pointer and that is not older than HORIZON. + nsecs_t minTime = newestMovement.eventTime - HORIZON; + uint32_t oldestIndex = mIndex; + uint32_t numTouches = 1; + do { + uint32_t nextOldestIndex = (oldestIndex == 0 ? HISTORY_SIZE : oldestIndex) - 1; + const Movement& nextOldestMovement = mMovements[nextOldestIndex]; + if (!nextOldestMovement.idBits.hasBit(id) + || nextOldestMovement.eventTime < minTime) { + break; + } + oldestIndex = nextOldestIndex; + } while (++numTouches < HISTORY_SIZE); + + // Calculate an exponentially weighted moving average of the velocity estimate + // at different points in time measured relative to the oldest sample. + // This is essentially an IIR filter. Newer samples are weighted more heavily + // than older samples. Samples at equal time points are weighted more or less + // equally. + // + // One tricky problem is that the sample data may be poorly conditioned. + // Sometimes samples arrive very close together in time which can cause us to + // overestimate the velocity at that time point. Most samples might be measured + // 16ms apart but some consecutive samples could be only 0.5sm apart because + // the hardware or driver reports them irregularly or in bursts. + float accumVx = 0; + float accumVy = 0; + uint32_t index = oldestIndex; + uint32_t samplesUsed = 0; + const Movement& oldestMovement = mMovements[oldestIndex]; + const VelocityTracker::Position& oldestPosition = oldestMovement.getPosition(id); + nsecs_t lastDuration = 0; + + while (numTouches-- > 1) { + if (++index == HISTORY_SIZE) { + index = 0; + } + const Movement& movement = mMovements[index]; + nsecs_t duration = movement.eventTime - oldestMovement.eventTime; + + // If the duration between samples is small, we may significantly overestimate + // the velocity. Consequently, we impose a minimum duration constraint on the + // samples that we include in the calculation. + if (duration >= MIN_DURATION) { + const VelocityTracker::Position& position = movement.getPosition(id); + float scale = 1000000000.0f / duration; // one over time delta in seconds + float vx = (position.x - oldestPosition.x) * scale; + float vy = (position.y - oldestPosition.y) * scale; + accumVx = (accumVx * lastDuration + vx * duration) / (duration + lastDuration); + accumVy = (accumVy * lastDuration + vy * duration) / (duration + lastDuration); + lastDuration = duration; + samplesUsed += 1; + } + } + + // Report velocity. + const VelocityTracker::Position& newestPosition = newestMovement.getPosition(id); + outEstimator->time = newestMovement.eventTime; + outEstimator->confidence = 1; + outEstimator->xCoeff[0] = newestPosition.x; + outEstimator->yCoeff[0] = newestPosition.y; + if (samplesUsed) { + outEstimator->xCoeff[1] = accumVx; + outEstimator->yCoeff[1] = accumVy; + outEstimator->degree = 1; + } else { + outEstimator->degree = 0; + } + return true; +} + +} // namespace android diff --git a/widget/gonk/libui/VelocityTracker.h b/widget/gonk/libui/VelocityTracker.h new file mode 100644 index 000000000000..fd077d4385c4 --- /dev/null +++ b/widget/gonk/libui/VelocityTracker.h @@ -0,0 +1,269 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _ANDROIDFW_VELOCITY_TRACKER_H +#define _ANDROIDFW_VELOCITY_TRACKER_H + +#include "Input.h" +#include +#include + +namespace android { + +class VelocityTrackerStrategy; + +/* + * Calculates the velocity of pointer movements over time. + */ +class VelocityTracker { +public: + struct Position { + float x, y; + }; + + struct Estimator { + static const size_t MAX_DEGREE = 4; + + // Estimator time base. + nsecs_t time; + + // Polynomial coefficients describing motion in X and Y. + float xCoeff[MAX_DEGREE + 1], yCoeff[MAX_DEGREE + 1]; + + // Polynomial degree (number of coefficients), or zero if no information is + // available. + uint32_t degree; + + // Confidence (coefficient of determination), between 0 (no fit) and 1 (perfect fit). + float confidence; + + inline void clear() { + time = 0; + degree = 0; + confidence = 0; + for (size_t i = 0; i <= MAX_DEGREE; i++) { + xCoeff[i] = 0; + yCoeff[i] = 0; + } + } + }; + + // Creates a velocity tracker using the specified strategy. + // If strategy is NULL, uses the default strategy for the platform. + VelocityTracker(const char* strategy = NULL); + + ~VelocityTracker(); + + // Resets the velocity tracker state. + void clear(); + + // Resets the velocity tracker state for specific pointers. + // Call this method when some pointers have changed and may be reusing + // an id that was assigned to a different pointer earlier. + void clearPointers(BitSet32 idBits); + + // Adds movement information for a set of pointers. + // The idBits bitfield specifies the pointer ids of the pointers whose positions + // are included in the movement. + // The positions array contains position information for each pointer in order by + // increasing id. Its size should be equal to the number of one bits in idBits. + void addMovement(nsecs_t eventTime, BitSet32 idBits, const Position* positions); + + // Adds movement information for all pointers in a MotionEvent, including historical samples. + void addMovement(const MotionEvent* event); + + // Gets the velocity of the specified pointer id in position units per second. + // Returns false and sets the velocity components to zero if there is + // insufficient movement information for the pointer. + bool getVelocity(uint32_t id, float* outVx, float* outVy) const; + + // Gets an estimator for the recent movements of the specified pointer id. + // Returns false and clears the estimator if there is no information available + // about the pointer. + bool getEstimator(uint32_t id, Estimator* outEstimator) const; + + // Gets the active pointer id, or -1 if none. + inline int32_t getActivePointerId() const { return mActivePointerId; } + + // Gets a bitset containing all pointer ids from the most recent movement. + inline BitSet32 getCurrentPointerIdBits() const { return mCurrentPointerIdBits; } + +private: + static const char* DEFAULT_STRATEGY; + + nsecs_t mLastEventTime; + BitSet32 mCurrentPointerIdBits; + int32_t mActivePointerId; + VelocityTrackerStrategy* mStrategy; + + bool configureStrategy(const char* strategy); + + static VelocityTrackerStrategy* createStrategy(const char* strategy); +}; + + +/* + * Implements a particular velocity tracker algorithm. + */ +class VelocityTrackerStrategy { +protected: + VelocityTrackerStrategy() { } + +public: + virtual ~VelocityTrackerStrategy() { } + + virtual void clear() = 0; + virtual void clearPointers(BitSet32 idBits) = 0; + virtual void addMovement(nsecs_t eventTime, BitSet32 idBits, + const VelocityTracker::Position* positions) = 0; + virtual bool getEstimator(uint32_t id, VelocityTracker::Estimator* outEstimator) const = 0; +}; + + +/* + * Velocity tracker algorithm based on least-squares linear regression. + */ +class LeastSquaresVelocityTrackerStrategy : public VelocityTrackerStrategy { +public: + enum Weighting { + // No weights applied. All data points are equally reliable. + WEIGHTING_NONE, + + // Weight by time delta. Data points clustered together are weighted less. + WEIGHTING_DELTA, + + // Weight such that points within a certain horizon are weighed more than those + // outside of that horizon. + WEIGHTING_CENTRAL, + + // Weight such that points older than a certain amount are weighed less. + WEIGHTING_RECENT, + }; + + // Degree must be no greater than Estimator::MAX_DEGREE. + LeastSquaresVelocityTrackerStrategy(uint32_t degree, Weighting weighting = WEIGHTING_NONE); + virtual ~LeastSquaresVelocityTrackerStrategy(); + + virtual void clear(); + virtual void clearPointers(BitSet32 idBits); + virtual void addMovement(nsecs_t eventTime, BitSet32 idBits, + const VelocityTracker::Position* positions); + virtual bool getEstimator(uint32_t id, VelocityTracker::Estimator* outEstimator) const; + +private: + // Sample horizon. + // We don't use too much history by default since we want to react to quick + // changes in direction. + static const nsecs_t HORIZON = 100 * 1000000; // 100 ms + + // Number of samples to keep. + static const uint32_t HISTORY_SIZE = 20; + + struct Movement { + nsecs_t eventTime; + BitSet32 idBits; + VelocityTracker::Position positions[MAX_POINTERS]; + + inline const VelocityTracker::Position& getPosition(uint32_t id) const { + return positions[idBits.getIndexOfBit(id)]; + } + }; + + float chooseWeight(uint32_t index) const; + + const uint32_t mDegree; + const Weighting mWeighting; + uint32_t mIndex; + Movement mMovements[HISTORY_SIZE]; +}; + + +/* + * Velocity tracker algorithm that uses an IIR filter. + */ +class IntegratingVelocityTrackerStrategy : public VelocityTrackerStrategy { +public: + // Degree must be 1 or 2. + IntegratingVelocityTrackerStrategy(uint32_t degree); + ~IntegratingVelocityTrackerStrategy(); + + virtual void clear(); + virtual void clearPointers(BitSet32 idBits); + virtual void addMovement(nsecs_t eventTime, BitSet32 idBits, + const VelocityTracker::Position* positions); + virtual bool getEstimator(uint32_t id, VelocityTracker::Estimator* outEstimator) const; + +private: + // Current state estimate for a particular pointer. + struct State { + nsecs_t updateTime; + uint32_t degree; + + float xpos, xvel, xaccel; + float ypos, yvel, yaccel; + }; + + const uint32_t mDegree; + BitSet32 mPointerIdBits; + State mPointerState[MAX_POINTER_ID + 1]; + + void initState(State& state, nsecs_t eventTime, float xpos, float ypos) const; + void updateState(State& state, nsecs_t eventTime, float xpos, float ypos) const; + void populateEstimator(const State& state, VelocityTracker::Estimator* outEstimator) const; +}; + + +/* + * Velocity tracker strategy used prior to ICS. + */ +class LegacyVelocityTrackerStrategy : public VelocityTrackerStrategy { +public: + LegacyVelocityTrackerStrategy(); + virtual ~LegacyVelocityTrackerStrategy(); + + virtual void clear(); + virtual void clearPointers(BitSet32 idBits); + virtual void addMovement(nsecs_t eventTime, BitSet32 idBits, + const VelocityTracker::Position* positions); + virtual bool getEstimator(uint32_t id, VelocityTracker::Estimator* outEstimator) const; + +private: + // Oldest sample to consider when calculating the velocity. + static const nsecs_t HORIZON = 200 * 1000000; // 100 ms + + // Number of samples to keep. + static const uint32_t HISTORY_SIZE = 20; + + // The minimum duration between samples when estimating velocity. + static const nsecs_t MIN_DURATION = 10 * 1000000; // 10 ms + + struct Movement { + nsecs_t eventTime; + BitSet32 idBits; + VelocityTracker::Position positions[MAX_POINTERS]; + + inline const VelocityTracker::Position& getPosition(uint32_t id) const { + return positions[idBits.getIndexOfBit(id)]; + } + }; + + uint32_t mIndex; + Movement mMovements[HISTORY_SIZE]; +}; + +} // namespace android + +#endif // _ANDROIDFW_VELOCITY_TRACKER_H diff --git a/widget/gonk/libui/VirtualKeyMap.cpp b/widget/gonk/libui/VirtualKeyMap.cpp index b91ceebdfa38..444ab3718765 100644 --- a/widget/gonk/libui/VirtualKeyMap.cpp +++ b/widget/gonk/libui/VirtualKeyMap.cpp @@ -15,14 +15,14 @@ */ #define LOG_TAG "VirtualKeyMap" +#include "cutils_log.h" #include #include -#include "utils_Log.h" #include "VirtualKeyMap.h" #include #include "Tokenizer.h" -#include "Timers.h" +#include // Enables debug output for the parser. #define DEBUG_PARSER 0 diff --git a/widget/gonk/libui/VirtualKeyMap.h b/widget/gonk/libui/VirtualKeyMap.h index 2fbc123b7656..79d61a536c25 100644 --- a/widget/gonk/libui/VirtualKeyMap.h +++ b/widget/gonk/libui/VirtualKeyMap.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef _UI_VIRTUAL_KEY_MAP_H -#define _UI_VIRTUAL_KEY_MAP_H +#ifndef _ANDROIDFW_VIRTUAL_KEY_MAP_H +#define _ANDROIDFW_VIRTUAL_KEY_MAP_H #include @@ -23,8 +23,8 @@ #include #include #include "Tokenizer.h" -#include "String8.h" -#include "Unicode.h" +#include +#include namespace android { @@ -43,6 +43,8 @@ struct VirtualKeyDefinition { /** * Describes a collection of virtual keys on a touch screen in terms of * virtual scan codes and hit rectangles. + * + * This object is immutable after it has been loaded. */ class VirtualKeyMap { public: @@ -76,4 +78,4 @@ private: } // namespace android -#endif // _UI_KEY_CHARACTER_MAP_H +#endif // _ANDROIDFW_KEY_CHARACTER_MAP_H diff --git a/widget/gonk/libui/android_input.h b/widget/gonk/libui/android_input.h index 5f27df48d47d..00e81b28d89c 100644 --- a/widget/gonk/libui/android_input.h +++ b/widget/gonk/libui/android_input.h @@ -428,6 +428,7 @@ enum { enum { AINPUT_SOURCE_CLASS_MASK = 0x000000ff, + AINPUT_SOURCE_CLASS_NONE = 0x00000000, AINPUT_SOURCE_CLASS_BUTTON = 0x00000001, AINPUT_SOURCE_CLASS_POINTER = 0x00000002, AINPUT_SOURCE_CLASS_NAVIGATION = 0x00000004, @@ -446,6 +447,7 @@ enum { AINPUT_SOURCE_STYLUS = 0x00004000 | AINPUT_SOURCE_CLASS_POINTER, AINPUT_SOURCE_TRACKBALL = 0x00010000 | AINPUT_SOURCE_CLASS_NAVIGATION, AINPUT_SOURCE_TOUCHPAD = 0x00100000 | AINPUT_SOURCE_CLASS_POSITION, + AINPUT_SOURCE_TOUCH_NAVIGATION = 0x00200000 | AINPUT_SOURCE_CLASS_NONE, AINPUT_SOURCE_JOYSTICK = 0x01000000 | AINPUT_SOURCE_CLASS_JOYSTICK, AINPUT_SOURCE_ANY = 0xffffff00, diff --git a/widget/gonk/libui/android_keycodes.h b/widget/gonk/libui/android_keycodes.h index 282e729a845a..cf38d1af483f 100644 --- a/widget/gonk/libui/android_keycodes.h +++ b/widget/gonk/libui/android_keycodes.h @@ -263,6 +263,8 @@ enum { AKEYCODE_RO = 217, AKEYCODE_KANA = 218, AKEYCODE_ASSIST = 219, + AKEYCODE_BRIGHTNESS_DOWN = 220, + AKEYCODE_BRIGHTNESS_UP = 221, // NOTE: If you add a new keycode here you must also add it to several other files. // Refer to frameworks/base/core/java/android/view/KeyEvent.java for the full list. diff --git a/widget/gonk/libui/cutils_log.h b/widget/gonk/libui/cutils_log.h index 2997a0cee107..8b045c75de82 100644 --- a/widget/gonk/libui/cutils_log.h +++ b/widget/gonk/libui/cutils_log.h @@ -79,10 +79,6 @@ extern "C" { #else #define ALOGV(...) ((void)ALOG(LOG_VERBOSE, LOG_TAG, __VA_ARGS__)) #endif -// Temporary measure for code still using old LOG macros. -#ifndef LOGV -#define LOGV ALOGV -#endif #endif #define CONDITION(cond) (__builtin_expect((cond)!=0, 0)) @@ -96,10 +92,6 @@ extern "C" { ? ((void)ALOG(LOG_VERBOSE, LOG_TAG, __VA_ARGS__)) \ : (void)0 ) #endif -// Temporary measure for code still using old LOG macros. -#ifndef LOGV_IF -#define LOGV_IF ALOGV_IF -#endif #endif /* @@ -107,10 +99,6 @@ extern "C" { */ #ifndef ALOGD #define ALOGD(...) ((void)ALOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__)) -// Temporary measure for code still using old LOG macros. -#ifndef LOGD -#define LOGD ALOGD -#endif #endif #ifndef ALOGD_IF @@ -118,10 +106,6 @@ extern "C" { ( (CONDITION(cond)) \ ? ((void)ALOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__)) \ : (void)0 ) -// Temporary measure for code still using old LOG macros. -#ifndef LOGD_IF -#define LOGD_IF ALOGD_IF -#endif #endif /* @@ -129,10 +113,6 @@ extern "C" { */ #ifndef ALOGI #define ALOGI(...) ((void)ALOG(LOG_INFO, LOG_TAG, __VA_ARGS__)) -// Temporary measure for code still using old LOG macros. -#ifndef LOGI -#define LOGI ALOGI -#endif #endif #ifndef ALOGI_IF @@ -140,10 +120,6 @@ extern "C" { ( (CONDITION(cond)) \ ? ((void)ALOG(LOG_INFO, LOG_TAG, __VA_ARGS__)) \ : (void)0 ) -// Temporary measure for code still using old LOG macros. -#ifndef LOGI_IF -#define LOGI_IF ALOGI_IF -#endif #endif /* @@ -151,10 +127,6 @@ extern "C" { */ #ifndef ALOGW #define ALOGW(...) ((void)ALOG(LOG_WARN, LOG_TAG, __VA_ARGS__)) -// Temporary measure for code still using old LOG macros. -#ifndef LOGW -#define LOGW ALOGW -#endif #endif #ifndef ALOGW_IF @@ -162,10 +134,6 @@ extern "C" { ( (CONDITION(cond)) \ ? ((void)ALOG(LOG_WARN, LOG_TAG, __VA_ARGS__)) \ : (void)0 ) -// Temporary measure for code still using old LOG macros. -#ifndef LOGW_IF -#define LOGW_IF ALOGW_IF -#endif #endif /* @@ -173,10 +141,6 @@ extern "C" { */ #ifndef ALOGE #define ALOGE(...) ((void)ALOG(LOG_ERROR, LOG_TAG, __VA_ARGS__)) -// Temporary measure for code still using old LOG macros. -#ifndef LOGE -#define LOGE ALOGE -#endif #endif #ifndef ALOGE_IF @@ -184,10 +148,6 @@ extern "C" { ( (CONDITION(cond)) \ ? ((void)ALOG(LOG_ERROR, LOG_TAG, __VA_ARGS__)) \ : (void)0 ) -// Temporary measure for code still using old LOG macros. -#ifndef LOGE_IF -#define LOGE_IF ALOGE_IF -#endif #endif // --------------------------------------------------------------------- @@ -202,10 +162,6 @@ extern "C" { #else #define IF_ALOGV() IF_ALOG(LOG_VERBOSE, LOG_TAG) #endif -// Temporary measure for code still using old LOG macros. -#ifndef IF_LOGV -#define IF_LOGV IF_ALOGV -#endif #endif /* @@ -214,10 +170,6 @@ extern "C" { */ #ifndef IF_ALOGD #define IF_ALOGD() IF_ALOG(LOG_DEBUG, LOG_TAG) -// Temporary measure for code still using old LOG macros. -#ifndef IF_LOGD -#define IF_LOGD IF_ALOGD -#endif #endif /* @@ -226,10 +178,6 @@ extern "C" { */ #ifndef IF_ALOGI #define IF_ALOGI() IF_ALOG(LOG_INFO, LOG_TAG) -// Temporary measure for code still using old LOG macros. -#ifndef IF_LOGI -#define IF_LOGI IF_ALOGI -#endif #endif /* @@ -238,10 +186,6 @@ extern "C" { */ #ifndef IF_ALOGW #define IF_ALOGW() IF_ALOG(LOG_WARN, LOG_TAG) -// Temporary measure for code still using old LOG macros. -#ifndef IF_LOGW -#define IF_LOGW IF_ALOGW -#endif #endif /* @@ -250,10 +194,6 @@ extern "C" { */ #ifndef IF_ALOGE #define IF_ALOGE() IF_ALOG(LOG_ERROR, LOG_TAG) -// Temporary measure for code still using old LOG macros. -#ifndef IF_LOGE -#define IF_LOGE IF_ALOGE -#endif #endif @@ -339,7 +279,88 @@ extern "C" { : (void)0 ) #endif - +// --------------------------------------------------------------------- + +/* + * Simplified macro to send a verbose radio log message using the current LOG_TAG. + */ +#ifndef RLOGV +#if LOG_NDEBUG +#define RLOGV(...) ((void)0) +#else +#define RLOGV(...) ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__)) +#endif +#endif + +#define CONDITION(cond) (__builtin_expect((cond)!=0, 0)) + +#ifndef RLOGV_IF +#if LOG_NDEBUG +#define RLOGV_IF(cond, ...) ((void)0) +#else +#define RLOGV_IF(cond, ...) \ + ( (CONDITION(cond)) \ + ? ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__)) \ + : (void)0 ) +#endif +#endif + +/* + * Simplified macro to send a debug radio log message using the current LOG_TAG. + */ +#ifndef RLOGD +#define RLOGD(...) ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)) +#endif + +#ifndef RLOGD_IF +#define RLOGD_IF(cond, ...) \ + ( (CONDITION(cond)) \ + ? ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)) \ + : (void)0 ) +#endif + +/* + * Simplified macro to send an info radio log message using the current LOG_TAG. + */ +#ifndef RLOGI +#define RLOGI(...) ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)) +#endif + +#ifndef RLOGI_IF +#define RLOGI_IF(cond, ...) \ + ( (CONDITION(cond)) \ + ? ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)) \ + : (void)0 ) +#endif + +/* + * Simplified macro to send a warning radio log message using the current LOG_TAG. + */ +#ifndef RLOGW +#define RLOGW(...) ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)) +#endif + +#ifndef RLOGW_IF +#define RLOGW_IF(cond, ...) \ + ( (CONDITION(cond)) \ + ? ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)) \ + : (void)0 ) +#endif + +/* + * Simplified macro to send an error radio log message using the current LOG_TAG. + */ +#ifndef RLOGE +#define RLOGE(...) ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)) +#endif + +#ifndef RLOGE_IF +#define RLOGE_IF(cond, ...) \ + ( (CONDITION(cond)) \ + ? ((void)__android_log_buf_print(LOG_ID_RADIO, ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)) \ + : (void)0 ) +#endif + // --------------------------------------------------------------------- @@ -392,10 +413,6 @@ extern "C" { #ifndef ALOG_ASSERT #define ALOG_ASSERT(cond, ...) LOG_FATAL_IF(!(cond), ## __VA_ARGS__) //#define ALOG_ASSERT(cond) LOG_FATAL_IF(!(cond), "Assertion failed: " #cond) -// Temporary measure for code still using old LOG macros. -#ifndef LOG_ASSERT -#define LOG_ASSERT ALOG_ASSERT -#endif #endif // --------------------------------------------------------------------- @@ -411,10 +428,6 @@ extern "C" { #ifndef ALOG #define ALOG(priority, tag, ...) \ LOG_PRI(ANDROID_##priority, tag, __VA_ARGS__) -// Temporary measure for code still using old LOG macros. -#ifndef LOG -#define LOG ALOG -#endif #endif /* @@ -439,10 +452,6 @@ extern "C" { #ifndef IF_ALOG #define IF_ALOG(priority, tag) \ if (android_testLog(ANDROID_##priority, tag)) -// Temporary measure for code still using old LOG macros. -#ifndef IF_LOG -#define IF_LOG IF_ALOG -#endif #endif // --------------------------------------------------------------------- diff --git a/widget/gonk/libui/cutils_trace.h b/widget/gonk/libui/cutils_trace.h new file mode 100644 index 000000000000..29034cab5f41 --- /dev/null +++ b/widget/gonk/libui/cutils_trace.h @@ -0,0 +1,276 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _LIBS_CUTILS_TRACE_H +#define _LIBS_CUTILS_TRACE_H + +#include +#include +#include +#include +#include +#include + +#ifdef ANDROID_SMP +#include +#else +#include +#endif + +__BEGIN_DECLS + +/** + * The ATRACE_TAG macro can be defined before including this header to trace + * using one of the tags defined below. It must be defined to one of the + * following ATRACE_TAG_* macros. The trace tag is used to filter tracing in + * userland to avoid some of the runtime cost of tracing when it is not desired. + * + * Defining ATRACE_TAG to be ATRACE_TAG_ALWAYS will result in the tracing always + * being enabled - this should ONLY be done for debug code, as userland tracing + * has a performance cost even when the trace is not being recorded. Defining + * ATRACE_TAG to be ATRACE_TAG_NEVER or leaving ATRACE_TAG undefined will result + * in the tracing always being disabled. + * + * ATRACE_TAG_HAL should be bitwise ORed with the relevant tags for tracing + * within a hardware module. For example a camera hardware module would set: + * #define ATRACE_TAG (ATRACE_TAG_CAMERA | ATRACE_TAG_HAL) + * + * Keep these in sync with frameworks/base/core/java/android/os/Trace.java. + */ +#define ATRACE_TAG_NEVER 0 // This tag is never enabled. +#define ATRACE_TAG_ALWAYS (1<<0) // This tag is always enabled. +#define ATRACE_TAG_GRAPHICS (1<<1) +#define ATRACE_TAG_INPUT (1<<2) +#define ATRACE_TAG_VIEW (1<<3) +#define ATRACE_TAG_WEBVIEW (1<<4) +#define ATRACE_TAG_WINDOW_MANAGER (1<<5) +#define ATRACE_TAG_ACTIVITY_MANAGER (1<<6) +#define ATRACE_TAG_SYNC_MANAGER (1<<7) +#define ATRACE_TAG_AUDIO (1<<8) +#define ATRACE_TAG_VIDEO (1<<9) +#define ATRACE_TAG_CAMERA (1<<10) +#define ATRACE_TAG_HAL (1<<11) +#define ATRACE_TAG_APP (1<<12) +#define ATRACE_TAG_RESOURCES (1<<13) +#define ATRACE_TAG_DALVIK (1<<14) +#define ATRACE_TAG_LAST ATRACE_TAG_DALVIK + +// Reserved for initialization. +#define ATRACE_TAG_NOT_READY (1LL<<63) + +#define ATRACE_TAG_VALID_MASK ((ATRACE_TAG_LAST - 1) | ATRACE_TAG_LAST) + +#ifndef ATRACE_TAG +#define ATRACE_TAG ATRACE_TAG_NEVER +#elif ATRACE_TAG > ATRACE_TAG_VALID_MASK +#error ATRACE_TAG must be defined to be one of the tags defined in cutils/trace.h +#endif + +#ifdef HAVE_ANDROID_OS +/** + * Maximum size of a message that can be logged to the trace buffer. + * Note this message includes a tag, the pid, and the string given as the name. + * Names should be kept short to get the most use of the trace buffer. + */ +#define ATRACE_MESSAGE_LENGTH 1024 + +/** + * Opens the trace file for writing and reads the property for initial tags. + * The atrace.tags.enableflags property sets the tags to trace. + * This function should not be explicitly called, the first call to any normal + * trace function will cause it to be run safely. + */ +void atrace_setup(); + +/** + * If tracing is ready, set atrace_enabled_tags to the system property + * debug.atrace.tags.enableflags. Can be used as a sysprop change callback. + */ +void atrace_update_tags(); + +/** + * Set whether the process is debuggable. By default the process is not + * considered debuggable. If the process is not debuggable then application- + * level tracing is not allowed unless the ro.debuggable system property is + * set to '1'. + */ +void atrace_set_debuggable(bool debuggable); + +/** + * Set whether tracing is enabled for the current process. This is used to + * prevent tracing within the Zygote process. + */ +void atrace_set_tracing_enabled(bool enabled); + +/** + * Flag indicating whether setup has been completed, initialized to 0. + * Nonzero indicates setup has completed. + * Note: This does NOT indicate whether or not setup was successful. + */ +extern volatile int32_t atrace_is_ready; + +/** + * Set of ATRACE_TAG flags to trace for, initialized to ATRACE_TAG_NOT_READY. + * A value of zero indicates setup has failed. + * Any other nonzero value indicates setup has succeeded, and tracing is on. + */ +extern uint64_t atrace_enabled_tags; + +/** + * Handle to the kernel's trace buffer, initialized to -1. + * Any other value indicates setup has succeeded, and is a valid fd for tracing. + */ +extern int atrace_marker_fd; + +/** + * atrace_init readies the process for tracing by opening the trace_marker file. + * Calling any trace function causes this to be run, so calling it is optional. + * This can be explicitly run to avoid setup delay on first trace function. + */ +#define ATRACE_INIT() atrace_init() +static inline void atrace_init() +{ + if (CC_UNLIKELY(!android_atomic_acquire_load(&atrace_is_ready))) { + atrace_setup(); + } +} + +/** + * Get the mask of all tags currently enabled. + * It can be used as a guard condition around more expensive trace calculations. + * Every trace function calls this, which ensures atrace_init is run. + */ +#define ATRACE_GET_ENABLED_TAGS() atrace_get_enabled_tags() +static inline uint64_t atrace_get_enabled_tags() +{ + atrace_init(); + return atrace_enabled_tags; +} + +/** + * Test if a given tag is currently enabled. + * Returns nonzero if the tag is enabled, otherwise zero. + * It can be used as a guard condition around more expensive trace calculations. + */ +#define ATRACE_ENABLED() atrace_is_tag_enabled(ATRACE_TAG) +static inline uint64_t atrace_is_tag_enabled(uint64_t tag) +{ + return atrace_get_enabled_tags() & tag; +} + +/** + * Trace the beginning of a context. name is used to identify the context. + * This is often used to time function execution. + */ +#define ATRACE_BEGIN(name) atrace_begin(ATRACE_TAG, name) +static inline void atrace_begin(uint64_t tag, const char* name) +{ + if (CC_UNLIKELY(atrace_is_tag_enabled(tag))) { + char buf[ATRACE_MESSAGE_LENGTH]; + size_t len; + + len = snprintf(buf, ATRACE_MESSAGE_LENGTH, "B|%d|%s", getpid(), name); + write(atrace_marker_fd, buf, len); + } +} + +/** + * Trace the end of a context. + * This should match up (and occur after) a corresponding ATRACE_BEGIN. + */ +#define ATRACE_END() atrace_end(ATRACE_TAG) +static inline void atrace_end(uint64_t tag) +{ + if (CC_UNLIKELY(atrace_is_tag_enabled(tag))) { + char c = 'E'; + write(atrace_marker_fd, &c, 1); + } +} + +/** + * Trace the beginning of an asynchronous event. Unlike ATRACE_BEGIN/ATRACE_END + * contexts, asynchronous events do not need to be nested. The name describes + * the event, and the cookie provides a unique identifier for distinguishing + * simultaneous events. The name and cookie used to begin an event must be + * used to end it. + */ +#define ATRACE_ASYNC_BEGIN(name, cookie) \ + atrace_async_begin(ATRACE_TAG, name, cookie) +static inline void atrace_async_begin(uint64_t tag, const char* name, + int32_t cookie) +{ + if (CC_UNLIKELY(atrace_is_tag_enabled(tag))) { + char buf[ATRACE_MESSAGE_LENGTH]; + size_t len; + + len = snprintf(buf, ATRACE_MESSAGE_LENGTH, "S|%d|%s|%d", getpid(), + name, cookie); + write(atrace_marker_fd, buf, len); + } +} + +/** + * Trace the end of an asynchronous event. + * This should have a corresponding ATRACE_ASYNC_BEGIN. + */ +#define ATRACE_ASYNC_END(name, cookie) atrace_async_end(ATRACE_TAG, name, cookie) +static inline void atrace_async_end(uint64_t tag, const char* name, + int32_t cookie) +{ + if (CC_UNLIKELY(atrace_is_tag_enabled(tag))) { + char buf[ATRACE_MESSAGE_LENGTH]; + size_t len; + + len = snprintf(buf, ATRACE_MESSAGE_LENGTH, "F|%d|%s|%d", getpid(), + name, cookie); + write(atrace_marker_fd, buf, len); + } +} + + +/** + * Traces an integer counter value. name is used to identify the counter. + * This can be used to track how a value changes over time. + */ +#define ATRACE_INT(name, value) atrace_int(ATRACE_TAG, name, value) +static inline void atrace_int(uint64_t tag, const char* name, int32_t value) +{ + if (CC_UNLIKELY(atrace_is_tag_enabled(tag))) { + char buf[ATRACE_MESSAGE_LENGTH]; + size_t len; + + len = snprintf(buf, ATRACE_MESSAGE_LENGTH, "C|%d|%s|%d", + getpid(), name, value); + write(atrace_marker_fd, buf, len); + } +} + +#else // not HAVE_ANDROID_OS + +#define ATRACE_INIT() +#define ATRACE_GET_ENABLED_TAGS() +#define ATRACE_ENABLED() +#define ATRACE_BEGIN(name) +#define ATRACE_END() +#define ATRACE_ASYNC_BEGIN(name, cookie) +#define ATRACE_ASYNC_END(name, cookie) +#define ATRACE_INT(name, value) + +#endif // not HAVE_ANDROID_OS + +__END_DECLS + +#endif // _LIBS_CUTILS_TRACE_H diff --git a/widget/gonk/libui/linux_input.h b/widget/gonk/libui/linux_input.h index 4045eb0c8ea7..2ba14973f5e6 100644 --- a/widget/gonk/libui/linux_input.h +++ b/widget/gonk/libui/linux_input.h @@ -20,796 +20,1010 @@ #define _INPUT_H #include #include +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #include #include struct input_event { struct timeval time; +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ __u16 type; __u16 code; __s32 value; }; +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define EV_VERSION 0x010001 struct input_id { __u16 bustype; __u16 vendor; +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ __u16 product; __u16 version; }; struct input_absinfo { +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ __s32 value; __s32 minimum; __s32 maximum; __s32 fuzz; +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ __s32 flat; __s32 resolution; }; struct input_keymap_entry { +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define INPUT_KEYMAP_BY_INDEX (1 << 0) __u8 flags; __u8 len; __u16 index; +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ __u32 keycode; __u8 scancode[32]; }; #define EVIOCGVERSION _IOR('E', 0x01, int) +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define EVIOCGID _IOR('E', 0x02, struct input_id) #define EVIOCGREP _IOR('E', 0x03, unsigned int[2]) #define EVIOCSREP _IOW('E', 0x03, unsigned int[2]) #define EVIOCGKEYCODE _IOR('E', 0x04, unsigned int[2]) +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define EVIOCGKEYCODE_V2 _IOR('E', 0x04, struct input_keymap_entry) #define EVIOCSKEYCODE _IOW('E', 0x04, unsigned int[2]) #define EVIOCSKEYCODE_V2 _IOW('E', 0x04, struct input_keymap_entry) #define EVIOCGNAME(len) _IOC(_IOC_READ, 'E', 0x06, len) +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define EVIOCGPHYS(len) _IOC(_IOC_READ, 'E', 0x07, len) #define EVIOCGUNIQ(len) _IOC(_IOC_READ, 'E', 0x08, len) #define EVIOCGPROP(len) _IOC(_IOC_READ, 'E', 0x09, len) +#define EVIOCGMTSLOTS(len) _IOC(_IOC_READ, 'E', 0x0a, len) +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define EVIOCGKEY(len) _IOC(_IOC_READ, 'E', 0x18, len) #define EVIOCGLED(len) _IOC(_IOC_READ, 'E', 0x19, len) #define EVIOCGSND(len) _IOC(_IOC_READ, 'E', 0x1a, len) #define EVIOCGSW(len) _IOC(_IOC_READ, 'E', 0x1b, len) -#define EVIOCGBIT(ev,len) _IOC(_IOC_READ, 'E', 0x20 + ev, len) -#define EVIOCGABS(abs) _IOR('E', 0x40 + abs, struct input_absinfo) -#define EVIOCSABS(abs) _IOW('E', 0xc0 + abs, struct input_absinfo) +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define EVIOCGBIT(ev,len) _IOC(_IOC_READ, 'E', 0x20 + (ev), len) +#define EVIOCGABS(abs) _IOR('E', 0x40 + (abs), struct input_absinfo) +#define EVIOCSABS(abs) _IOW('E', 0xc0 + (abs), struct input_absinfo) #define EVIOCSFF _IOC(_IOC_WRITE, 'E', 0x80, sizeof(struct ff_effect)) +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define EVIOCRMFF _IOW('E', 0x81, int) #define EVIOCGEFFECTS _IOR('E', 0x84, int) #define EVIOCGRAB _IOW('E', 0x90, int) +#define EVIOCGSUSPENDBLOCK _IOR('E', 0x91, int) +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define EVIOCSSUSPENDBLOCK _IOW('E', 0x91, int) +#define EVIOCSCLOCKID _IOW('E', 0xa0, int) #define INPUT_PROP_POINTER 0x00 #define INPUT_PROP_DIRECT 0x01 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define INPUT_PROP_BUTTONPAD 0x02 #define INPUT_PROP_SEMI_MT 0x03 #define INPUT_PROP_MAX 0x1f #define INPUT_PROP_CNT (INPUT_PROP_MAX + 1) +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define EV_SYN 0x00 #define EV_KEY 0x01 #define EV_REL 0x02 #define EV_ABS 0x03 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define EV_MSC 0x04 #define EV_SW 0x05 #define EV_LED 0x11 #define EV_SND 0x12 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define EV_REP 0x14 #define EV_FF 0x15 #define EV_PWR 0x16 #define EV_FF_STATUS 0x17 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define EV_MAX 0x1f #define EV_CNT (EV_MAX+1) #define SYN_REPORT 0 #define SYN_CONFIG 1 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define SYN_MT_REPORT 2 #define SYN_DROPPED 3 +#define SYN_TIME_SEC 4 +#define SYN_TIME_NSEC 5 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define KEY_RESERVED 0 #define KEY_ESC 1 #define KEY_1 2 #define KEY_2 3 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define KEY_3 4 #define KEY_4 5 #define KEY_5 6 #define KEY_6 7 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define KEY_7 8 #define KEY_8 9 #define KEY_9 10 #define KEY_0 11 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define KEY_MINUS 12 #define KEY_EQUAL 13 #define KEY_BACKSPACE 14 #define KEY_TAB 15 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define KEY_Q 16 #define KEY_W 17 #define KEY_E 18 #define KEY_R 19 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define KEY_T 20 #define KEY_Y 21 #define KEY_U 22 #define KEY_I 23 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define KEY_O 24 #define KEY_P 25 #define KEY_LEFTBRACE 26 #define KEY_RIGHTBRACE 27 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define KEY_ENTER 28 #define KEY_LEFTCTRL 29 #define KEY_A 30 #define KEY_S 31 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define KEY_D 32 #define KEY_F 33 #define KEY_G 34 #define KEY_H 35 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define KEY_J 36 #define KEY_K 37 #define KEY_L 38 #define KEY_SEMICOLON 39 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define KEY_APOSTROPHE 40 #define KEY_GRAVE 41 #define KEY_LEFTSHIFT 42 #define KEY_BACKSLASH 43 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define KEY_Z 44 #define KEY_X 45 #define KEY_C 46 #define KEY_V 47 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define KEY_B 48 #define KEY_N 49 #define KEY_M 50 #define KEY_COMMA 51 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define KEY_DOT 52 #define KEY_SLASH 53 #define KEY_RIGHTSHIFT 54 #define KEY_KPASTERISK 55 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define KEY_LEFTALT 56 #define KEY_SPACE 57 #define KEY_CAPSLOCK 58 #define KEY_F1 59 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define KEY_F2 60 #define KEY_F3 61 #define KEY_F4 62 #define KEY_F5 63 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define KEY_F6 64 #define KEY_F7 65 #define KEY_F8 66 #define KEY_F9 67 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define KEY_F10 68 #define KEY_NUMLOCK 69 #define KEY_SCROLLLOCK 70 #define KEY_KP7 71 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define KEY_KP8 72 #define KEY_KP9 73 #define KEY_KPMINUS 74 #define KEY_KP4 75 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define KEY_KP5 76 #define KEY_KP6 77 #define KEY_KPPLUS 78 #define KEY_KP1 79 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define KEY_KP2 80 #define KEY_KP3 81 #define KEY_KP0 82 #define KEY_KPDOT 83 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define KEY_ZENKAKUHANKAKU 85 #define KEY_102ND 86 #define KEY_F11 87 #define KEY_F12 88 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define KEY_RO 89 #define KEY_KATAKANA 90 #define KEY_HIRAGANA 91 #define KEY_HENKAN 92 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define KEY_KATAKANAHIRAGANA 93 #define KEY_MUHENKAN 94 #define KEY_KPJPCOMMA 95 #define KEY_KPENTER 96 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define KEY_RIGHTCTRL 97 #define KEY_KPSLASH 98 #define KEY_SYSRQ 99 #define KEY_RIGHTALT 100 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define KEY_LINEFEED 101 #define KEY_HOME 102 #define KEY_UP 103 #define KEY_PAGEUP 104 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define KEY_LEFT 105 #define KEY_RIGHT 106 #define KEY_END 107 #define KEY_DOWN 108 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define KEY_PAGEDOWN 109 #define KEY_INSERT 110 #define KEY_DELETE 111 #define KEY_MACRO 112 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define KEY_MUTE 113 #define KEY_VOLUMEDOWN 114 #define KEY_VOLUMEUP 115 #define KEY_POWER 116 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define KEY_KPEQUAL 117 #define KEY_KPPLUSMINUS 118 #define KEY_PAUSE 119 #define KEY_SCALE 120 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define KEY_KPCOMMA 121 #define KEY_HANGEUL 122 #define KEY_HANGUEL KEY_HANGEUL #define KEY_HANJA 123 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define KEY_YEN 124 #define KEY_LEFTMETA 125 #define KEY_RIGHTMETA 126 #define KEY_COMPOSE 127 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define KEY_STOP 128 #define KEY_AGAIN 129 #define KEY_PROPS 130 #define KEY_UNDO 131 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define KEY_FRONT 132 #define KEY_COPY 133 #define KEY_OPEN 134 #define KEY_PASTE 135 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define KEY_FIND 136 #define KEY_CUT 137 #define KEY_HELP 138 #define KEY_MENU 139 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define KEY_CALC 140 #define KEY_SETUP 141 #define KEY_SLEEP 142 #define KEY_WAKEUP 143 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define KEY_FILE 144 #define KEY_SENDFILE 145 #define KEY_DELETEFILE 146 #define KEY_XFER 147 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define KEY_PROG1 148 #define KEY_PROG2 149 #define KEY_WWW 150 #define KEY_MSDOS 151 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define KEY_COFFEE 152 #define KEY_SCREENLOCK KEY_COFFEE #define KEY_DIRECTION 153 #define KEY_CYCLEWINDOWS 154 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define KEY_MAIL 155 #define KEY_BOOKMARKS 156 #define KEY_COMPUTER 157 #define KEY_BACK 158 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define KEY_FORWARD 159 #define KEY_CLOSECD 160 #define KEY_EJECTCD 161 #define KEY_EJECTCLOSECD 162 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define KEY_NEXTSONG 163 #define KEY_PLAYPAUSE 164 #define KEY_PREVIOUSSONG 165 #define KEY_STOPCD 166 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define KEY_RECORD 167 #define KEY_REWIND 168 #define KEY_PHONE 169 #define KEY_ISO 170 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define KEY_CONFIG 171 #define KEY_HOMEPAGE 172 #define KEY_REFRESH 173 #define KEY_EXIT 174 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define KEY_MOVE 175 #define KEY_EDIT 176 #define KEY_SCROLLUP 177 #define KEY_SCROLLDOWN 178 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define KEY_KPLEFTPAREN 179 #define KEY_KPRIGHTPAREN 180 #define KEY_NEW 181 #define KEY_REDO 182 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define KEY_F13 183 #define KEY_F14 184 #define KEY_F15 185 #define KEY_F16 186 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define KEY_F17 187 #define KEY_F18 188 #define KEY_F19 189 #define KEY_F20 190 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define KEY_F21 191 #define KEY_F22 192 #define KEY_F23 193 #define KEY_F24 194 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define KEY_PLAYCD 200 #define KEY_PAUSECD 201 #define KEY_PROG3 202 #define KEY_PROG4 203 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define KEY_DASHBOARD 204 #define KEY_SUSPEND 205 #define KEY_CLOSE 206 #define KEY_PLAY 207 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define KEY_FASTFORWARD 208 #define KEY_BASSBOOST 209 #define KEY_PRINT 210 #define KEY_HP 211 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define KEY_CAMERA 212 #define KEY_SOUND 213 #define KEY_QUESTION 214 #define KEY_EMAIL 215 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define KEY_CHAT 216 #define KEY_SEARCH 217 #define KEY_CONNECT 218 #define KEY_FINANCE 219 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define KEY_SPORT 220 #define KEY_SHOP 221 #define KEY_ALTERASE 222 #define KEY_CANCEL 223 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define KEY_BRIGHTNESSDOWN 224 #define KEY_BRIGHTNESSUP 225 #define KEY_MEDIA 226 #define KEY_SWITCHVIDEOMODE 227 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define KEY_KBDILLUMTOGGLE 228 #define KEY_KBDILLUMDOWN 229 #define KEY_KBDILLUMUP 230 #define KEY_SEND 231 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define KEY_REPLY 232 #define KEY_FORWARDMAIL 233 #define KEY_SAVE 234 #define KEY_DOCUMENTS 235 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define KEY_BATTERY 236 #define KEY_BLUETOOTH 237 #define KEY_WLAN 238 #define KEY_UWB 239 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define KEY_UNKNOWN 240 #define KEY_VIDEO_NEXT 241 #define KEY_VIDEO_PREV 242 #define KEY_BRIGHTNESS_CYCLE 243 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define KEY_BRIGHTNESS_ZERO 244 #define KEY_DISPLAY_OFF 245 #define KEY_WIMAX 246 #define KEY_RFKILL 247 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define KEY_MICMUTE 248 #define BTN_MISC 0x100 #define BTN_0 0x100 #define BTN_1 0x101 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define BTN_2 0x102 #define BTN_3 0x103 #define BTN_4 0x104 #define BTN_5 0x105 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define BTN_6 0x106 #define BTN_7 0x107 #define BTN_8 0x108 #define BTN_9 0x109 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define BTN_MOUSE 0x110 #define BTN_LEFT 0x110 #define BTN_RIGHT 0x111 #define BTN_MIDDLE 0x112 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define BTN_SIDE 0x113 #define BTN_EXTRA 0x114 #define BTN_FORWARD 0x115 #define BTN_BACK 0x116 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define BTN_TASK 0x117 #define BTN_JOYSTICK 0x120 #define BTN_TRIGGER 0x120 #define BTN_THUMB 0x121 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define BTN_THUMB2 0x122 #define BTN_TOP 0x123 #define BTN_TOP2 0x124 #define BTN_PINKIE 0x125 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define BTN_BASE 0x126 #define BTN_BASE2 0x127 #define BTN_BASE3 0x128 #define BTN_BASE4 0x129 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define BTN_BASE5 0x12a #define BTN_BASE6 0x12b #define BTN_DEAD 0x12f #define BTN_GAMEPAD 0x130 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define BTN_A 0x130 #define BTN_B 0x131 #define BTN_C 0x132 #define BTN_X 0x133 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define BTN_Y 0x134 #define BTN_Z 0x135 #define BTN_TL 0x136 #define BTN_TR 0x137 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define BTN_TL2 0x138 #define BTN_TR2 0x139 #define BTN_SELECT 0x13a #define BTN_START 0x13b +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define BTN_MODE 0x13c #define BTN_THUMBL 0x13d #define BTN_THUMBR 0x13e #define BTN_DIGI 0x140 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define BTN_TOOL_PEN 0x140 #define BTN_TOOL_RUBBER 0x141 #define BTN_TOOL_BRUSH 0x142 #define BTN_TOOL_PENCIL 0x143 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define BTN_TOOL_AIRBRUSH 0x144 #define BTN_TOOL_FINGER 0x145 #define BTN_TOOL_MOUSE 0x146 #define BTN_TOOL_LENS 0x147 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define BTN_TOOL_QUINTTAP 0x148 #define BTN_TOUCH 0x14a #define BTN_STYLUS 0x14b #define BTN_STYLUS2 0x14c +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define BTN_TOOL_DOUBLETAP 0x14d #define BTN_TOOL_TRIPLETAP 0x14e #define BTN_TOOL_QUADTAP 0x14f #define BTN_WHEEL 0x150 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define BTN_GEAR_DOWN 0x150 #define BTN_GEAR_UP 0x151 #define KEY_OK 0x160 #define KEY_SELECT 0x161 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define KEY_GOTO 0x162 #define KEY_CLEAR 0x163 #define KEY_POWER2 0x164 #define KEY_OPTION 0x165 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define KEY_INFO 0x166 #define KEY_TIME 0x167 #define KEY_VENDOR 0x168 #define KEY_ARCHIVE 0x169 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define KEY_PROGRAM 0x16a #define KEY_CHANNEL 0x16b #define KEY_FAVORITES 0x16c #define KEY_EPG 0x16d +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define KEY_PVR 0x16e #define KEY_MHP 0x16f #define KEY_LANGUAGE 0x170 #define KEY_TITLE 0x171 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define KEY_SUBTITLE 0x172 #define KEY_ANGLE 0x173 #define KEY_ZOOM 0x174 #define KEY_MODE 0x175 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define KEY_KEYBOARD 0x176 #define KEY_SCREEN 0x177 #define KEY_PC 0x178 #define KEY_TV 0x179 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define KEY_TV2 0x17a #define KEY_VCR 0x17b #define KEY_VCR2 0x17c #define KEY_SAT 0x17d +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define KEY_SAT2 0x17e #define KEY_CD 0x17f #define KEY_TAPE 0x180 #define KEY_RADIO 0x181 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define KEY_TUNER 0x182 #define KEY_PLAYER 0x183 #define KEY_TEXT 0x184 #define KEY_DVD 0x185 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define KEY_AUX 0x186 #define KEY_MP3 0x187 #define KEY_AUDIO 0x188 #define KEY_VIDEO 0x189 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define KEY_DIRECTORY 0x18a #define KEY_LIST 0x18b #define KEY_MEMO 0x18c #define KEY_CALENDAR 0x18d +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define KEY_RED 0x18e #define KEY_GREEN 0x18f #define KEY_YELLOW 0x190 #define KEY_BLUE 0x191 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define KEY_CHANNELUP 0x192 #define KEY_CHANNELDOWN 0x193 #define KEY_FIRST 0x194 #define KEY_LAST 0x195 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define KEY_AB 0x196 #define KEY_NEXT 0x197 #define KEY_RESTART 0x198 #define KEY_SLOW 0x199 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define KEY_SHUFFLE 0x19a #define KEY_BREAK 0x19b #define KEY_PREVIOUS 0x19c #define KEY_DIGITS 0x19d +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define KEY_TEEN 0x19e #define KEY_TWEN 0x19f #define KEY_VIDEOPHONE 0x1a0 #define KEY_GAMES 0x1a1 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define KEY_ZOOMIN 0x1a2 #define KEY_ZOOMOUT 0x1a3 #define KEY_ZOOMRESET 0x1a4 #define KEY_WORDPROCESSOR 0x1a5 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define KEY_EDITOR 0x1a6 #define KEY_SPREADSHEET 0x1a7 #define KEY_GRAPHICSEDITOR 0x1a8 #define KEY_PRESENTATION 0x1a9 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define KEY_DATABASE 0x1aa #define KEY_NEWS 0x1ab #define KEY_VOICEMAIL 0x1ac #define KEY_ADDRESSBOOK 0x1ad +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define KEY_MESSENGER 0x1ae #define KEY_DISPLAYTOGGLE 0x1af #define KEY_SPELLCHECK 0x1b0 #define KEY_LOGOFF 0x1b1 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define KEY_DOLLAR 0x1b2 #define KEY_EURO 0x1b3 #define KEY_FRAMEBACK 0x1b4 #define KEY_FRAMEFORWARD 0x1b5 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define KEY_CONTEXT_MENU 0x1b6 #define KEY_MEDIA_REPEAT 0x1b7 #define KEY_10CHANNELSUP 0x1b8 #define KEY_10CHANNELSDOWN 0x1b9 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define KEY_IMAGES 0x1ba #define KEY_DEL_EOL 0x1c0 #define KEY_DEL_EOS 0x1c1 #define KEY_INS_LINE 0x1c2 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define KEY_DEL_LINE 0x1c3 #define KEY_FN 0x1d0 #define KEY_FN_ESC 0x1d1 #define KEY_FN_F1 0x1d2 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define KEY_FN_F2 0x1d3 #define KEY_FN_F3 0x1d4 #define KEY_FN_F4 0x1d5 #define KEY_FN_F5 0x1d6 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define KEY_FN_F6 0x1d7 #define KEY_FN_F7 0x1d8 #define KEY_FN_F8 0x1d9 #define KEY_FN_F9 0x1da +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define KEY_FN_F10 0x1db #define KEY_FN_F11 0x1dc #define KEY_FN_F12 0x1dd #define KEY_FN_1 0x1de +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define KEY_FN_2 0x1df #define KEY_FN_D 0x1e0 #define KEY_FN_E 0x1e1 #define KEY_FN_F 0x1e2 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define KEY_FN_S 0x1e3 #define KEY_FN_B 0x1e4 #define KEY_BRL_DOT1 0x1f1 #define KEY_BRL_DOT2 0x1f2 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define KEY_BRL_DOT3 0x1f3 #define KEY_BRL_DOT4 0x1f4 #define KEY_BRL_DOT5 0x1f5 #define KEY_BRL_DOT6 0x1f6 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define KEY_BRL_DOT7 0x1f7 #define KEY_BRL_DOT8 0x1f8 #define KEY_BRL_DOT9 0x1f9 #define KEY_BRL_DOT10 0x1fa +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define KEY_NUMERIC_0 0x200 #define KEY_NUMERIC_1 0x201 #define KEY_NUMERIC_2 0x202 #define KEY_NUMERIC_3 0x203 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define KEY_NUMERIC_4 0x204 #define KEY_NUMERIC_5 0x205 #define KEY_NUMERIC_6 0x206 #define KEY_NUMERIC_7 0x207 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define KEY_NUMERIC_8 0x208 #define KEY_NUMERIC_9 0x209 #define KEY_NUMERIC_STAR 0x20a #define KEY_NUMERIC_POUND 0x20b +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define KEY_CAMERA_SNAPSHOT 0x2fe #define KEY_CAMERA_FOCUS 0x210 #define KEY_WPS_BUTTON 0x211 #define KEY_TOUCHPAD_TOGGLE 0x212 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define KEY_TOUCHPAD_ON 0x213 #define KEY_TOUCHPAD_OFF 0x214 #define KEY_CAMERA_ZOOMIN 0x215 #define KEY_CAMERA_ZOOMOUT 0x216 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define KEY_CAMERA_UP 0x217 #define KEY_CAMERA_DOWN 0x218 #define KEY_CAMERA_LEFT 0x219 #define KEY_CAMERA_RIGHT 0x21a +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define BTN_TRIGGER_HAPPY 0x2c0 #define BTN_TRIGGER_HAPPY1 0x2c0 #define BTN_TRIGGER_HAPPY2 0x2c1 #define BTN_TRIGGER_HAPPY3 0x2c2 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define BTN_TRIGGER_HAPPY4 0x2c3 #define BTN_TRIGGER_HAPPY5 0x2c4 #define BTN_TRIGGER_HAPPY6 0x2c5 #define BTN_TRIGGER_HAPPY7 0x2c6 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define BTN_TRIGGER_HAPPY8 0x2c7 #define BTN_TRIGGER_HAPPY9 0x2c8 #define BTN_TRIGGER_HAPPY10 0x2c9 #define BTN_TRIGGER_HAPPY11 0x2ca +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define BTN_TRIGGER_HAPPY12 0x2cb #define BTN_TRIGGER_HAPPY13 0x2cc #define BTN_TRIGGER_HAPPY14 0x2cd #define BTN_TRIGGER_HAPPY15 0x2ce +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define BTN_TRIGGER_HAPPY16 0x2cf #define BTN_TRIGGER_HAPPY17 0x2d0 #define BTN_TRIGGER_HAPPY18 0x2d1 #define BTN_TRIGGER_HAPPY19 0x2d2 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define BTN_TRIGGER_HAPPY20 0x2d3 #define BTN_TRIGGER_HAPPY21 0x2d4 #define BTN_TRIGGER_HAPPY22 0x2d5 #define BTN_TRIGGER_HAPPY23 0x2d6 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define BTN_TRIGGER_HAPPY24 0x2d7 #define BTN_TRIGGER_HAPPY25 0x2d8 #define BTN_TRIGGER_HAPPY26 0x2d9 #define BTN_TRIGGER_HAPPY27 0x2da +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define BTN_TRIGGER_HAPPY28 0x2db #define BTN_TRIGGER_HAPPY29 0x2dc #define BTN_TRIGGER_HAPPY30 0x2dd #define BTN_TRIGGER_HAPPY31 0x2de +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define BTN_TRIGGER_HAPPY32 0x2df #define BTN_TRIGGER_HAPPY33 0x2e0 #define BTN_TRIGGER_HAPPY34 0x2e1 #define BTN_TRIGGER_HAPPY35 0x2e2 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define BTN_TRIGGER_HAPPY36 0x2e3 #define BTN_TRIGGER_HAPPY37 0x2e4 #define BTN_TRIGGER_HAPPY38 0x2e5 #define BTN_TRIGGER_HAPPY39 0x2e6 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define BTN_TRIGGER_HAPPY40 0x2e7 #define KEY_MIN_INTERESTING KEY_MUTE #define KEY_MAX 0x2ff #define KEY_CNT (KEY_MAX+1) +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define REL_X 0x00 #define REL_Y 0x01 #define REL_Z 0x02 #define REL_RX 0x03 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define REL_RY 0x04 #define REL_RZ 0x05 #define REL_HWHEEL 0x06 #define REL_DIAL 0x07 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define REL_WHEEL 0x08 #define REL_MISC 0x09 #define REL_MAX 0x0f #define REL_CNT (REL_MAX+1) +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define ABS_X 0x00 #define ABS_Y 0x01 #define ABS_Z 0x02 #define ABS_RX 0x03 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define ABS_RY 0x04 #define ABS_RZ 0x05 #define ABS_THROTTLE 0x06 #define ABS_RUDDER 0x07 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define ABS_WHEEL 0x08 #define ABS_GAS 0x09 #define ABS_BRAKE 0x0a #define ABS_HAT0X 0x10 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define ABS_HAT0Y 0x11 #define ABS_HAT1X 0x12 #define ABS_HAT1Y 0x13 #define ABS_HAT2X 0x14 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define ABS_HAT2Y 0x15 #define ABS_HAT3X 0x16 #define ABS_HAT3Y 0x17 #define ABS_PRESSURE 0x18 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define ABS_DISTANCE 0x19 #define ABS_TILT_X 0x1a #define ABS_TILT_Y 0x1b #define ABS_TOOL_WIDTH 0x1c +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define ABS_VOLUME 0x20 #define ABS_MISC 0x28 #define ABS_MT_SLOT 0x2f #define ABS_MT_TOUCH_MAJOR 0x30 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define ABS_MT_TOUCH_MINOR 0x31 #define ABS_MT_WIDTH_MAJOR 0x32 #define ABS_MT_WIDTH_MINOR 0x33 #define ABS_MT_ORIENTATION 0x34 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define ABS_MT_POSITION_X 0x35 #define ABS_MT_POSITION_Y 0x36 #define ABS_MT_TOOL_TYPE 0x37 #define ABS_MT_BLOB_ID 0x38 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define ABS_MT_TRACKING_ID 0x39 #define ABS_MT_PRESSURE 0x3a #define ABS_MT_DISTANCE 0x3b #define ABS_MAX 0x3f +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define ABS_CNT (ABS_MAX+1) #define SW_LID 0x00 #define SW_TABLET_MODE 0x01 #define SW_HEADPHONE_INSERT 0x02 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define SW_RFKILL_ALL 0x03 #define SW_RADIO SW_RFKILL_ALL #define SW_MICROPHONE_INSERT 0x04 #define SW_DOCK 0x05 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define SW_LINEOUT_INSERT 0x06 #define SW_JACK_PHYSICAL_INSERT 0x07 #define SW_VIDEOOUT_INSERT 0x08 #define SW_CAMERA_LENS_COVER 0x09 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define SW_KEYPAD_SLIDE 0x0a #define SW_FRONT_PROXIMITY 0x0b #define SW_ROTATE_LOCK 0x0c -#define SW_MAX 0x0f +#define SW_LINEIN_INSERT 0x0d +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ +#define SW_HPHL_OVERCURRENT 0x0e +#define SW_HPHR_OVERCURRENT 0x0f +#define SW_UNSUPPORT_INSERT 0x10 +#define SW_MAX 0x20 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define SW_CNT (SW_MAX+1) #define MSC_SERIAL 0x00 #define MSC_PULSELED 0x01 #define MSC_GESTURE 0x02 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define MSC_RAW 0x03 #define MSC_SCAN 0x04 #define MSC_MAX 0x07 #define MSC_CNT (MSC_MAX+1) +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define LED_NUML 0x00 #define LED_CAPSL 0x01 #define LED_SCROLLL 0x02 #define LED_COMPOSE 0x03 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define LED_KANA 0x04 #define LED_SLEEP 0x05 #define LED_SUSPEND 0x06 #define LED_MUTE 0x07 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define LED_MISC 0x08 #define LED_MAIL 0x09 #define LED_CHARGING 0x0a #define LED_MAX 0x0f +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define LED_CNT (LED_MAX+1) #define REP_DELAY 0x00 #define REP_PERIOD 0x01 #define REP_MAX 0x01 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define REP_CNT (REP_MAX+1) #define SND_CLICK 0x00 #define SND_BELL 0x01 #define SND_TONE 0x02 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define SND_MAX 0x07 #define SND_CNT (SND_MAX+1) #define ID_BUS 0 #define ID_VENDOR 1 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define ID_PRODUCT 2 #define ID_VERSION 3 #define BUS_PCI 0x01 #define BUS_ISAPNP 0x02 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define BUS_USB 0x03 #define BUS_HIL 0x04 #define BUS_BLUETOOTH 0x05 #define BUS_VIRTUAL 0x06 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define BUS_ISA 0x10 #define BUS_I8042 0x11 #define BUS_XTKBD 0x12 #define BUS_RS232 0x13 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define BUS_GAMEPORT 0x14 #define BUS_PARPORT 0x15 #define BUS_AMIGA 0x16 #define BUS_ADB 0x17 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define BUS_I2C 0x18 #define BUS_HOST 0x19 #define BUS_GSC 0x1A #define BUS_ATARI 0x1B +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define BUS_SPI 0x1C #define MT_TOOL_FINGER 0 #define MT_TOOL_PEN 1 #define MT_TOOL_MAX 1 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define FF_STATUS_STOPPED 0x00 #define FF_STATUS_PLAYING 0x01 #define FF_STATUS_MAX 0x01 struct ff_replay { +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ __u16 length; __u16 delay; }; struct ff_trigger { +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ __u16 button; __u16 interval; }; struct ff_envelope { +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ __u16 attack_length; __u16 attack_level; __u16 fade_length; __u16 fade_level; +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ }; struct ff_constant_effect { __s16 level; struct ff_envelope envelope; +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ }; struct ff_ramp_effect { __s16 start_level; __s16 end_level; +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ struct ff_envelope envelope; }; struct ff_condition_effect { __u16 right_saturation; +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ __u16 left_saturation; __s16 right_coeff; __s16 left_coeff; __u16 deadband; +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ __s16 center; }; struct ff_periodic_effect { __u16 waveform; +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ __u16 period; __s16 magnitude; __s16 offset; __u16 phase; +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ struct ff_envelope envelope; __u32 custom_len; - __s16 *custom_data; + __s16 __user *custom_data; }; +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ struct ff_rumble_effect { __u16 strong_magnitude; __u16 weak_magnitude; }; +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ struct ff_effect { __u16 type; __s16 id; __u16 direction; +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ struct ff_trigger trigger; struct ff_replay replay; union { struct ff_constant_effect constant; +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ struct ff_ramp_effect ramp; struct ff_periodic_effect periodic; struct ff_condition_effect condition[2]; struct ff_rumble_effect rumble; +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ } u; }; #define FF_RUMBLE 0x50 #define FF_PERIODIC 0x51 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define FF_CONSTANT 0x52 #define FF_SPRING 0x53 #define FF_FRICTION 0x54 #define FF_DAMPER 0x55 +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define FF_INERTIA 0x56 #define FF_RAMP 0x57 #define FF_EFFECT_MIN FF_RUMBLE #define FF_EFFECT_MAX FF_RAMP +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define FF_SQUARE 0x58 #define FF_TRIANGLE 0x59 #define FF_SINE 0x5a #define FF_SAW_UP 0x5b +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define FF_SAW_DOWN 0x5c #define FF_CUSTOM 0x5d #define FF_WAVEFORM_MIN FF_SQUARE #define FF_WAVEFORM_MAX FF_CUSTOM +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #define FF_GAIN 0x60 #define FF_AUTOCENTER 0x61 #define FF_MAX 0x7f #define FF_CNT (FF_MAX+1) +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ #endif - diff --git a/widget/gonk/libui/utils_Log.h b/widget/gonk/libui/utils_Log.h deleted file mode 100644 index 66ea7d313071..000000000000 --- a/widget/gonk/libui/utils_Log.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (C) 2005 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// -// C/C++ logging functions. See the logging documentation for API details. -// -// We'd like these to be available from C code (in case we import some from -// somewhere), so this has a C interface. -// -// The output will be correct when the log file is shared between multiple -// threads and/or multiple processes so long as the operating system -// supports O_APPEND. These calls have mutex-protected data structures -// and so are NOT reentrant. Do not use LOG in a signal handler. -// -#ifndef _LIBS_UTILS_LOG_H -#define _LIBS_UTILS_LOG_H - -#include "cutils_log.h" - -#endif // _LIBS_UTILS_LOG_H diff --git a/widget/gonk/moz.build b/widget/gonk/moz.build index cdd978bf49fe..4e22d6595cde 100644 --- a/widget/gonk/moz.build +++ b/widget/gonk/moz.build @@ -22,13 +22,12 @@ EXPORTS += [ DIRS += ['libdisplay', 'nativewindow'] +# libui files CPP_SOURCES += [ 'EventHub.cpp', - 'Framebuffer.cpp', - 'GonkMemoryPressureMonitoring.cpp', - 'HwcUtils.cpp', 'Input.cpp', 'InputApplication.cpp', + 'InputDevice.cpp', 'InputDispatcher.cpp', 'InputListener.cpp', 'InputReader.cpp', @@ -37,25 +36,26 @@ CPP_SOURCES += [ 'KeyCharacterMap.cpp', 'KeyLayoutMap.cpp', 'Keyboard.cpp', + 'PointerController.cpp', + 'SpriteController.cpp', + 'Tokenizer.cpp', + 'VelocityControl.cpp', + 'VelocityTracker.cpp', + 'VirtualKeyMap.cpp', +] + +CPP_SOURCES += [ + 'Framebuffer.cpp', + 'GfxInfo.cpp', + 'HwcUtils.cpp', + 'GonkMemoryPressureMonitoring.cpp', 'OrientationObserver.cpp', 'ProcessOrientation.cpp', - 'PixelFormat.cpp', - 'PointerController.cpp', - 'PropertyMap.cpp', - 'SpriteController.cpp', - 'Static.cpp', - 'String16.cpp', - 'String8.cpp', - 'Timers.cpp', - 'Tokenizer.cpp', - 'Unicode.cpp', - 'VirtualKeyMap.cpp', 'nsAppShell.cpp', 'nsIdleServiceGonk.cpp', 'nsLookAndFeel.cpp', 'nsWidgetFactory.cpp', 'nsWindow.cpp', - 'GfxInfo.cpp', ] if CONFIG['ANDROID_VERSION'] == '15': diff --git a/widget/gonk/nsAppShell.cpp b/widget/gonk/nsAppShell.cpp index 87b4ceb2d074..c71e2afeeea1 100644 --- a/widget/gonk/nsAppShell.cpp +++ b/widget/gonk/nsAppShell.cpp @@ -321,16 +321,18 @@ GeckoPointerController::getBounds(float* outMinX, { int32_t width, height, orientation; - mConfig->getDisplayInfo(0, false, &width, &height, &orientation); + DisplayViewport viewport; + + mConfig->getDisplayInfo(false, &viewport); *outMinX = *outMinY = 0; if (orientation == DISPLAY_ORIENTATION_90 || orientation == DISPLAY_ORIENTATION_270) { - *outMaxX = height; - *outMaxY = width; + *outMaxX = viewport.deviceHeight; + *outMaxY = viewport.deviceWidth; } else { - *outMaxX = width; - *outMaxY = height; + *outMaxX = viewport.deviceWidth; + *outMaxY = viewport.deviceHeight; } return true; } @@ -382,6 +384,16 @@ deviceId) { return new GeckoPointerController(&mConfig); }; + virtual void notifyInputDevicesChanged(const android::Vector& inputDevices) {}; + virtual sp getKeyboardLayoutOverlay(const String8& inputDeviceDescriptor) + { + return NULL; + }; + virtual String8 getDeviceAlias(const InputDeviceIdentifier& identifier) + { + return String8::empty(); + }; + void setDisplayInfo(); protected: @@ -454,7 +466,11 @@ GeckoInputReaderPolicy::setDisplayInfo() DISPLAY_ORIENTATION_270, "Orientation enums not matched!"); - mConfig.setDisplayInfo(0, false, gScreenBounds.width, gScreenBounds.height, nsScreenGonk::GetRotation()); + DisplayViewport viewport; + viewport.setNonDisplayViewport(gScreenBounds.width, gScreenBounds.height); + viewport.displayId = 0; + viewport.orientation = nsScreenGonk::GetRotation(); + mConfig.setDisplayInfo(false, viewport); } void GeckoInputReaderPolicy::getReaderConfiguration(InputReaderConfiguration* outConfig) @@ -596,16 +612,22 @@ void GeckoInputDispatcher::notifySwitch(const NotifySwitchArgs* args) if (!sDevInputAudioJack) return; - switch (args->switchCode) { - case SW_HEADPHONE_INSERT: - sHeadphoneState = args->switchValue; - updateHeadphoneSwitch(); - break; - case SW_MICROPHONE_INSERT: - sMicrophoneState = args->switchValue; - updateHeadphoneSwitch(); - break; + bool needSwitchUpdate = false; + + if (args->switchMask & (1 << SW_HEADPHONE_INSERT)) { + sHeadphoneState = (args->switchValues & (1 << SW_HEADPHONE_INSERT)) ? + AKEY_STATE_DOWN : AKEY_STATE_UP; + needSwitchUpdate = true; } + + if (args->switchMask & (1 << SW_MICROPHONE_INSERT)) { + sMicrophoneState = (args->switchValues & (1 << SW_MICROPHONE_INSERT)) ? + AKEY_STATE_DOWN : AKEY_STATE_UP; + needSwitchUpdate = true; + } + + if (needSwitchUpdate) + updateHeadphoneSwitch(); } void GeckoInputDispatcher::notifyDeviceReset(const NotifyDeviceResetArgs* args) diff --git a/widget/windows/winrt/APZController.cpp b/widget/windows/winrt/APZController.cpp new file mode 100644 index 000000000000..a9bad89671ab --- /dev/null +++ b/widget/windows/winrt/APZController.cpp @@ -0,0 +1,107 @@ +/* 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 "APZController.h" +#include "base/message_loop.h" +#include "mozilla/layers/GeckoContentController.h" +#include "nsThreadUtils.h" +#include "MetroUtils.h" +#include "nsPrintfCString.h" + +namespace mozilla { +namespace widget { +namespace winrt { + +class RequestContentRepaintEvent : public nsRunnable +{ + typedef mozilla::layers::FrameMetrics FrameMetrics; + +public: + RequestContentRepaintEvent(const FrameMetrics& aFrameMetrics) : mFrameMetrics(aFrameMetrics) + { + } + + NS_IMETHOD Run() { + // This event shuts down the worker thread and so must be main thread. + MOZ_ASSERT(NS_IsMainThread()); + + CSSToScreenScale resolution = mFrameMetrics.mZoom; + CSSRect compositedRect = mFrameMetrics.CalculateCompositedRectInCssPixels(); + + NS_ConvertASCIItoUTF16 data(nsPrintfCString("{ " \ + " \"resolution\": %.2f, " \ + " \"scrollId\": %d, " \ + " \"compositedRect\": { \"width\": %d, \"height\": %d }, " \ + " \"displayPort\": { \"x\": %d, \"y\": %d, \"width\": %d, \"height\": %d }, " \ + " \"scrollTo\": { \"x\": %d, \"y\": %d }" \ + "}", + (float)(resolution.scale / mFrameMetrics.mDevPixelsPerCSSPixel.scale), + (int)mFrameMetrics.mScrollId, + (int)compositedRect.width, + (int)compositedRect.height, + (int)mFrameMetrics.mDisplayPort.x, + (int)mFrameMetrics.mDisplayPort.y, + (int)mFrameMetrics.mDisplayPort.width, + (int)mFrameMetrics.mDisplayPort.height, + (int)mFrameMetrics.mScrollOffset.x, + (int)mFrameMetrics.mScrollOffset.y)); + + MetroUtils::FireObserver("apzc-request-content-repaint", data.get()); + return NS_OK; + } +protected: + const FrameMetrics mFrameMetrics; +}; + +void +APZController::RequestContentRepaint(const FrameMetrics& aFrameMetrics) +{ + // Send the result back to the main thread so that it can shutdown + nsCOMPtr r1 = new RequestContentRepaintEvent(aFrameMetrics); + if (!NS_IsMainThread()) { + NS_DispatchToMainThread(r1); + } else { + r1->Run(); + } +} + +void +APZController::HandleDoubleTap(const CSSIntPoint& aPoint) +{ +} + +void +APZController::HandleSingleTap(const CSSIntPoint& aPoint) +{ +} + +void +APZController::HandleLongTap(const CSSIntPoint& aPoint) +{ +} + +void +APZController::SendAsyncScrollDOMEvent(FrameMetrics::ViewID aScrollId, const CSSRect &aContentRect, const CSSSize &aScrollableSize) +{ +} + +void +APZController::PostDelayedTask(Task* aTask, int aDelayMs) +{ + MessageLoop::current()->PostDelayedTask(FROM_HERE, aTask, aDelayMs); +} + +void +APZController::HandlePanBegin() +{ + MetroUtils::FireObserver("apzc-handle-pan-begin", L""); +} + +void +APZController::HandlePanEnd() +{ + MetroUtils::FireObserver("apzc-handle-pan-end", L""); +} + +} } } \ No newline at end of file diff --git a/widget/windows/winrt/APZController.h b/widget/windows/winrt/APZController.h new file mode 100644 index 000000000000..8542b2fd94f1 --- /dev/null +++ b/widget/windows/winrt/APZController.h @@ -0,0 +1,32 @@ +/* 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/. */ + +#pragma once + +#include "mozwrlbase.h" + +#include "mozilla/layers/GeckoContentController.h" +#include "FrameMetrics.h" +#include "Units.h" + +namespace mozilla { +namespace widget { +namespace winrt { + +class APZController : public mozilla::layers::GeckoContentController +{ + typedef mozilla::layers::FrameMetrics FrameMetrics; + +public: + virtual void RequestContentRepaint(const FrameMetrics& aFrameMetrics); + virtual void HandleDoubleTap(const mozilla::CSSIntPoint& aPoint); + virtual void HandleSingleTap(const mozilla::CSSIntPoint& aPoint); + virtual void HandleLongTap(const mozilla::CSSIntPoint& aPoint); + virtual void SendAsyncScrollDOMEvent(FrameMetrics::ViewID aScrollId, const mozilla::CSSRect &aContentRect, const mozilla::CSSSize &aScrollableSize); + virtual void PostDelayedTask(Task* aTask, int aDelayMs); + virtual void HandlePanBegin(); + virtual void HandlePanEnd(); +}; + +} } } \ No newline at end of file diff --git a/widget/windows/winrt/FrameworkView.cpp b/widget/windows/winrt/FrameworkView.cpp index 76e3638f5bbf..49de0d97df09 100644 --- a/widget/windows/winrt/FrameworkView.cpp +++ b/widget/windows/winrt/FrameworkView.cpp @@ -197,7 +197,6 @@ FrameworkView::ShutdownXPCOM() mAutomationProvider = nullptr; mMetroInput = nullptr; - mD2DWindowSurface = nullptr; delete sSettingsArray; sSettingsArray = nullptr; mWidget = nullptr; diff --git a/widget/windows/winrt/FrameworkView.h b/widget/windows/winrt/FrameworkView.h index b3f56cf6f082..f1fcb1e745d7 100644 --- a/widget/windows/winrt/FrameworkView.h +++ b/widget/windows/winrt/FrameworkView.h @@ -7,6 +7,7 @@ #include "nsGUIEvent.h" #include "MetroWidget.h" +#include "MetroInput.h" #include "gfxWindowsPlatform.h" #include "gfxD2DSurface.h" #include "nsDataHashtable.h" @@ -176,7 +177,6 @@ private: EventRegistrationToken mPrintManager; private: - nsRefPtr mD2DWindowSurface; nsIntRect mWindowBounds; // in device-pixel coordinates float mDPI; bool mShuttingDown; diff --git a/widget/windows/winrt/MetroApp.cpp b/widget/windows/winrt/MetroApp.cpp index 776e72e70459..7d9dbefb3ecc 100644 --- a/widget/windows/winrt/MetroApp.cpp +++ b/widget/windows/winrt/MetroApp.cpp @@ -107,6 +107,7 @@ MetroApp::ShutdownXPCOM() void MetroApp::CoreExit() { + LogFunction(); HRESULT hr; ComPtr coreExit; HStringReference className(RuntimeClass_Windows_ApplicationModel_Core_CoreApplication); diff --git a/widget/windows/winrt/MetroInput.cpp b/widget/windows/winrt/MetroInput.cpp index 69a3fb7bf876..d57192b543e0 100644 --- a/widget/windows/winrt/MetroInput.cpp +++ b/widget/windows/winrt/MetroInput.cpp @@ -4,6 +4,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ // Moz headers (alphabetical) +#include "MetroInput.h" #include "MetroUtils.h" // Logging, POINT_CEIL_*, ActivateGenericInstance, etc #include "MetroWidget.h" // MetroInput::mWidget #include "mozilla/dom/Touch.h" // Touch @@ -161,8 +162,6 @@ MetroInput::MetroInput(MetroWidget* aWidget, NS_ASSERTION(aWidget, "Attempted to create MetroInput for null widget!"); NS_ASSERTION(aWindow, "Attempted to create MetroInput for null window!"); - mWidget->SetMetroInput(this); - mTokenPointerPressed.value = 0; mTokenPointerReleased.value = 0; mTokenPointerMoved.value = 0; @@ -928,27 +927,6 @@ MetroInput::OnRightTapped(UI::Input::IGestureRecognizer* aSender, return S_OK; } -// Used by MetroWidget GeckoContentController callbacks -void -MetroInput::HandleDoubleTap(const LayoutDeviceIntPoint& aPoint) -{ -#ifdef DEBUG_INPUT - LogFunction(); -#endif - nsSimpleGestureEvent* tapEvent = - new nsSimpleGestureEvent(true, - NS_SIMPLE_GESTURE_TAP, - mWidget.Get(), - 0, - 0.0); - - tapEvent->inputSource = nsIDOMMouseEvent::MOZ_SOURCE_TOUCH; - tapEvent->refPoint = aPoint; - tapEvent->clickCount = 2; - tapEvent->pressure = 1; - DispatchAsyncEventIgnoreStatus(tapEvent); -} - void MetroInput::HandleSingleTap(const LayoutDeviceIntPoint& aPoint) { diff --git a/widget/windows/winrt/MetroInput.h b/widget/windows/winrt/MetroInput.h index 826fc64e679e..cc9223ef4f8d 100644 --- a/widget/windows/winrt/MetroInput.h +++ b/widget/windows/winrt/MetroInput.h @@ -147,8 +147,6 @@ public: HRESULT OnRightTapped(IGestureRecognizer* aSender, IRightTappedEventArgs* aArgs); - // Used by MetroWidget GeckoContentController callbacks - void HandleDoubleTap(const mozilla::LayoutDeviceIntPoint& aPoint); void HandleSingleTap(const mozilla::LayoutDeviceIntPoint& aPoint); void HandleLongTap(const mozilla::LayoutDeviceIntPoint& aPoint); diff --git a/widget/windows/winrt/MetroWidget.cpp b/widget/windows/winrt/MetroWidget.cpp index 28b330a3bc53..f260eee4cf87 100644 --- a/widget/windows/winrt/MetroWidget.cpp +++ b/widget/windows/winrt/MetroWidget.cpp @@ -962,7 +962,8 @@ CompositorParent* MetroWidget::NewCompositorParent(int aSurfaceWidth, int aSurfa if (ShouldUseAPZC()) { mRootLayerTreeId = compositor->RootLayerTreeId(); - CompositorParent::SetControllerForLayerTree(mRootLayerTreeId, this); + mController = new APZController(); + CompositorParent::SetControllerForLayerTree(mRootLayerTreeId, mController); MetroWidget::sAPZC = CompositorParent::GetAPZCTreeManager(compositor->RootLayerTreeId()); MetroWidget::sAPZC->SetDPI(GetDPI()); @@ -1474,124 +1475,6 @@ MetroWidget::HasPendingInputEvent() return false; } -// GeckoContentController interface impl - -class RequestContentRepaintEvent : public nsRunnable -{ -public: - RequestContentRepaintEvent(const FrameMetrics& aFrameMetrics) : mFrameMetrics(aFrameMetrics) - { - } - - NS_IMETHOD Run() { - // This event shuts down the worker thread and so must be main thread. - MOZ_ASSERT(NS_IsMainThread()); - - CSSToScreenScale resolution = mFrameMetrics.mZoom; - CSSRect compositedRect = mFrameMetrics.CalculateCompositedRectInCssPixels(); - - NS_ConvertASCIItoUTF16 data(nsPrintfCString("{ " \ - " \"resolution\": %.2f, " \ - " \"scrollId\": %d, " \ - " \"compositedRect\": { \"width\": %d, \"height\": %d }, " \ - " \"displayPort\": { \"x\": %d, \"y\": %d, \"width\": %d, \"height\": %d }, " \ - " \"scrollTo\": { \"x\": %d, \"y\": %d }" \ - "}", - (float)(resolution.scale / mFrameMetrics.mDevPixelsPerCSSPixel.scale), - (int)mFrameMetrics.mScrollId, - (int)compositedRect.width, - (int)compositedRect.height, - (int)mFrameMetrics.mDisplayPort.x, - (int)mFrameMetrics.mDisplayPort.y, - (int)mFrameMetrics.mDisplayPort.width, - (int)mFrameMetrics.mDisplayPort.height, - (int)mFrameMetrics.mScrollOffset.x, - (int)mFrameMetrics.mScrollOffset.y)); - - MetroUtils::FireObserver("apzc-request-content-repaint", data.get()); - return NS_OK; - } -protected: - const FrameMetrics mFrameMetrics; -}; - -void -MetroWidget::RequestContentRepaint(const FrameMetrics& aFrameMetrics) -{ - LogFunction(); - - // Send the result back to the main thread so that it can shutdown - nsCOMPtr r1 = new RequestContentRepaintEvent(aFrameMetrics); - if (!NS_IsMainThread()) { - NS_DispatchToMainThread(r1); - } else { - r1->Run(); - } -} - -void -MetroWidget::HandleDoubleTap(const CSSIntPoint& aPoint) -{ - LogFunction(); - - if (!mMetroInput) { - return; - } - - mMetroInput->HandleDoubleTap(CSSIntPointToLayoutDeviceIntPoint(aPoint)); -} - -void -MetroWidget::HandleSingleTap(const CSSIntPoint& aPoint) -{ - LogFunction(); - - if (!mMetroInput) { - return; - } - - mMetroInput->HandleSingleTap(CSSIntPointToLayoutDeviceIntPoint(aPoint)); -} - -void -MetroWidget::HandleLongTap(const CSSIntPoint& aPoint) -{ - LogFunction(); - - if (!mMetroInput) { - return; - } - - mMetroInput->HandleLongTap(CSSIntPointToLayoutDeviceIntPoint(aPoint)); -} - -void -MetroWidget::SendAsyncScrollDOMEvent(FrameMetrics::ViewID aScrollId, const CSSRect &aContentRect, const CSSSize &aScrollableSize) -{ - LogFunction(); -} - -void -MetroWidget::PostDelayedTask(Task* aTask, int aDelayMs) -{ - LogFunction(); - MessageLoop::current()->PostDelayedTask(FROM_HERE, aTask, aDelayMs); -} - -void -MetroWidget::HandlePanBegin() -{ - LogFunction(); - MetroUtils::FireObserver("apzc-handle-pan-begin", L""); -} - -void -MetroWidget::HandlePanEnd() -{ - LogFunction(); - MetroUtils::FireObserver("apzc-handle-pan-end", L""); -} - NS_IMETHODIMP MetroWidget::Observe(nsISupports *subject, const char *topic, const PRUnichar *data) { diff --git a/widget/windows/winrt/MetroWidget.h b/widget/windows/winrt/MetroWidget.h index c83b99b46128..b958a210fcde 100644 --- a/widget/windows/winrt/MetroWidget.h +++ b/widget/windows/winrt/MetroWidget.h @@ -22,11 +22,11 @@ #include "mozilla/a11y/Accessible.h" #endif #include "mozilla/layers/CompositorParent.h" -#include "mozilla/layers/GeckoContentController.h" #include "mozilla/layers/APZCTreeManager.h" #include "mozilla/layers/LayerManagerComposite.h" #include "Units.h" -#include "MetroInput.h" +#include "nsDeque.h" +#include "APZController.h" #include "mozwrlbase.h" @@ -48,7 +48,6 @@ class FrameworkView; class DispatchMsg; class MetroWidget : public nsWindowBase, - public mozilla::layers::GeckoContentController, public nsIObserver { typedef mozilla::widget::WindowHook WindowHook; @@ -58,7 +57,7 @@ class MetroWidget : public nsWindowBase, typedef ABI::Windows::UI::Core::IKeyEventArgs IKeyEventArgs; typedef ABI::Windows::UI::Core::ICharacterReceivedEventArgs ICharacterReceivedEventArgs; typedef mozilla::widget::winrt::FrameworkView FrameworkView; - typedef mozilla::layers::FrameMetrics FrameMetrics; + typedef mozilla::widget::winrt::APZController APZController; static LRESULT CALLBACK StaticWindowProcedure(HWND aWnd, UINT aMsg, WPARAM aWParan, LPARAM aLParam); @@ -200,21 +199,6 @@ public: nsresult RequestContentScroll(); void RequestContentRepaintImplMainThread(); - // GeckoContentController interface impl - virtual void RequestContentRepaint(const FrameMetrics& aFrameMetrics); - virtual void HandleDoubleTap(const mozilla::CSSIntPoint& aPoint); - virtual void HandleSingleTap(const mozilla::CSSIntPoint& aPoint); - virtual void HandleLongTap(const mozilla::CSSIntPoint& aPoint); - virtual void SendAsyncScrollDOMEvent(FrameMetrics::ViewID aScrollId, const mozilla::CSSRect &aContentRect, const mozilla::CSSSize &aScrollableSize); - virtual void PostDelayedTask(Task* aTask, int aDelayMs); - virtual void HandlePanBegin(); - virtual void HandlePanEnd(); - - void SetMetroInput(mozilla::widget::winrt::MetroInput* aMetroInput) - { - mMetroInput = aMetroInput; - } - protected: friend class FrameworkView; @@ -238,6 +222,16 @@ protected: void RemoveSubclass(); nsIWidgetListener* GetPaintListener(); + // Async event dispatching + void DispatchAsyncScrollEvent(DispatchMsg* aEvent); + void DeliverNextScrollEvent(); + void DeliverNextKeyboardEvent(); + DispatchMsg* CreateDispatchMsg(UINT aMsg, WPARAM aWParam, LPARAM aLParam); + +public: + static nsRefPtr sAPZC; + +protected: OleInitializeWrapper mOleInitializeWrapper; WindowHook mWindowHook; Microsoft::WRL::ComPtr mView; @@ -248,19 +242,8 @@ protected: static HWND sICoreHwnd; WNDPROC mMetroWndProc; bool mTempBasicLayerInUse; - Microsoft::WRL::ComPtr mMetroInput; - mozilla::layers::FrameMetrics mFrameMetrics; uint64_t mRootLayerTreeId; - - // Async event dispatching - void DispatchAsyncScrollEvent(DispatchMsg* aEvent); - void DeliverNextScrollEvent(); - void DeliverNextKeyboardEvent(); - DispatchMsg* CreateDispatchMsg(UINT aMsg, WPARAM aWParam, LPARAM aLParam); - nsDeque mMsgEventQueue; nsDeque mKeyEventQueue; - -public: - static nsRefPtr sAPZC; + nsRefPtr mController; }; diff --git a/widget/windows/winrt/moz.build b/widget/windows/winrt/moz.build index e899b282fadf..0f379880d6c0 100644 --- a/widget/windows/winrt/moz.build +++ b/widget/windows/winrt/moz.build @@ -19,6 +19,7 @@ CPP_SOURCES += [ 'UIABridge.cpp', 'nsMetroFilePicker.cpp', 'nsWinMetroUtils.cpp', + 'APZController.cpp', ] EXTRA_COMPONENTS += [