Merge m-c to mozilla-inbound.

This commit is contained in:
Ryan VanderMeulen 2013-09-06 21:14:34 -04:00
commit 21aafe3cc3
152 changed files with 9041 additions and 11009 deletions

View File

@ -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);

View File

@ -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);
}
};

View File

@ -1,4 +1,4 @@
{
"revision": "3a11e24a45501f1fcad106ef588fcc7262722644",
"revision": "a04ef5d325988b36c3fb088d160c389a1e8682e5",
"repo_path": "/integration/gaia-central"
}

View File

@ -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@

View File

@ -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

View File

@ -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");
}
},

View File

@ -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");

View File

@ -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

View File

@ -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

View File

@ -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);
};

View File

@ -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;

View File

@ -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;

View File

@ -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"

View File

@ -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': {

View File

@ -1483,7 +1483,7 @@ BluetoothHfpManager::OnSocketConnectSuccess(BluetoothSocket* aSocket)
}
nsCOMPtr<nsITelephonyProvider> provider =
do_GetService(TELEPHONY_PROVIDER_CONTRACTID);
do_GetService(NS_RILCONTENTHELPER_CONTRACTID);
NS_ENSURE_TRUE_VOID(provider);
provider->EnumerateCalls(mListener->GetListener());

View File

@ -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<nsITelephonyProvider> 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<nsITelephonyProvider> 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;
}

View File

@ -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);

View File

@ -40,6 +40,7 @@ DOM_SRCDIRS = \
ifdef MOZ_B2G_RIL
DOM_SRCDIRS += \
dom/system/gonk \
dom/telephony \
dom/wifi \
dom/icc/src \
$(NULL)

View File

@ -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()
{

View File

@ -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);

View File

@ -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<TelephonyParent*>(aActor)->Release();
return true;
}
PStorageParent*
ContentParent::AllocPStorageParent()
{

View File

@ -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);

View File

@ -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();

View File

@ -25,7 +25,6 @@ XPIDL_SOURCES += [
if CONFIG['MOZ_B2G_RIL']:
XPIDL_SOURCES += [
'nsIRilMobileMessageDatabaseService.idl',
'nsIRilSmsService.idl',
]
XPIDL_MODULE = 'dom_mobilemessage'

View File

@ -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);
};

View File

@ -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);
};

View File

@ -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

View File

@ -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

View File

@ -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)
{

View File

@ -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)
{

View File

@ -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<nsIRadioInterfaceLayer> 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

View File

@ -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<nsIRadioInterface> mRadioInterface;
nsTArray<nsString> mSilentNumbers;
};
} // namespace mobilemessage
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_mobilemessage_SmsService_h

File diff suppressed because it is too large Load Diff

View File

@ -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}

View File

@ -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)
{

View File

@ -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 += [

View File

@ -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',

View File

@ -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 \

View File

@ -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) {

File diff suppressed because it is too large Load Diff

View File

@ -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: <A string topic>,
* callback: <A callback that accepts two parameters -- topic and msg>,
* }
*/
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"];

View File

@ -74,7 +74,6 @@ EXTRA_COMPONENTS += [
]
EXTRA_JS_MODULES += [
'RilMessageManager.jsm',
'net_worker.js',
'ril_consts.js',
'ril_worker.js',

View File

@ -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)]

View File

@ -11,7 +11,6 @@
USING_TELEPHONY_NAMESPACE
using namespace mozilla::dom;
using mozilla::ErrorResult;
/* static */
already_AddRefed<CallEvent>

View File

@ -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

View File

@ -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<nsITelephonyProvider> 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<TelephonyCall> 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;
}

View File

@ -15,7 +15,6 @@
USING_TELEPHONY_NAMESPACE
using namespace mozilla::dom;
using mozilla::ErrorResult;
// static
already_AddRefed<TelephonyCall>

View File

@ -13,7 +13,6 @@
USING_TELEPHONY_NAMESPACE
using namespace mozilla::dom;
using mozilla::ErrorResult;
TelephonyCallGroup::TelephonyCallGroup()
: mCallState(nsITelephonyProvider::CALL_STATE_UNKNOWN)

View File

@ -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<nsITelephonyProvider>
TelephonyFactory::CreateTelephonyProvider()
{
nsCOMPtr<nsITelephonyProvider> 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();
}

View File

@ -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<nsITelephonyProvider> CreateTelephonyProvider();
};
END_TELEPHONY_NAMESPACE
#endif // mozilla_dom_telephony_TelephonyFactory_h

View File

@ -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]);

View File

@ -1,2 +0,0 @@
component {67d26434-d063-4d28-9f48-5b3189788155} TelephonyProvider.js
contract @mozilla.org/telephony/gonktelephonyprovider;1 {67d26434-d063-4d28-9f48-5b3189788155}

View File

@ -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 */

View File

@ -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 */

View File

@ -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;
}

View File

@ -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<nsITelephonyListener> 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<nsITelephonyListener> mListener;
};
END_TELEPHONY_NAMESPACE
#endif // mozilla_dom_telephony_TelephonyChild_h

View File

@ -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;
}

View File

@ -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<nsCOMPtr<nsITelephonyListener> > mListeners;
PTelephonyChild* mPTelephonyChild;
};
END_TELEPHONY_NAMESPACE
#endif // mozilla_dom_telephony_TelephonyIPCProvider_h

View File

@ -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<TelephonyRequestParent*>(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<TelephonyRequestParent*>(aActor)->Release();
return true;
}
bool
TelephonyParent::Recv__delete__()
{
return true; // Unregister listener in TelephonyParent::ActorDestroy().
}
bool
TelephonyParent::RecvRegisterListener()
{
NS_ENSURE_TRUE(!mRegistered, true);
nsCOMPtr<nsITelephonyProvider> 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<nsITelephonyProvider> 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<nsITelephonyProvider> 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<nsITelephonyProvider> provider =
do_GetService(TELEPHONY_PROVIDER_CONTRACTID);
NS_ENSURE_TRUE(provider, true);
provider->HangUp(aCallIndex);
return true;
}
bool
TelephonyParent::RecvAnswerCall(const uint32_t& aCallIndex)
{
nsCOMPtr<nsITelephonyProvider> provider =
do_GetService(TELEPHONY_PROVIDER_CONTRACTID);
NS_ENSURE_TRUE(provider, true);
provider->AnswerCall(aCallIndex);
return true;
}
bool
TelephonyParent::RecvRejectCall(const uint32_t& aCallIndex)
{
nsCOMPtr<nsITelephonyProvider> provider =
do_GetService(TELEPHONY_PROVIDER_CONTRACTID);
NS_ENSURE_TRUE(provider, true);
provider->RejectCall(aCallIndex);
return true;
}
bool
TelephonyParent::RecvHoldCall(const uint32_t& aCallIndex)
{
nsCOMPtr<nsITelephonyProvider> provider =
do_GetService(TELEPHONY_PROVIDER_CONTRACTID);
NS_ENSURE_TRUE(provider, true);
provider->HoldCall(aCallIndex);
return true;
}
bool
TelephonyParent::RecvResumeCall(const uint32_t& aCallIndex)
{
nsCOMPtr<nsITelephonyProvider> provider =
do_GetService(TELEPHONY_PROVIDER_CONTRACTID);
NS_ENSURE_TRUE(provider, true);
provider->ResumeCall(aCallIndex);
return true;
}
bool
TelephonyParent::RecvConferenceCall()
{
nsCOMPtr<nsITelephonyProvider> provider =
do_GetService(TELEPHONY_PROVIDER_CONTRACTID);
NS_ENSURE_TRUE(provider, true);
provider->ConferenceCall();
return true;
}
bool
TelephonyParent::RecvSeparateCall(const uint32_t& aCallState)
{
nsCOMPtr<nsITelephonyProvider> provider =
do_GetService(TELEPHONY_PROVIDER_CONTRACTID);
NS_ENSURE_TRUE(provider, true);
provider->SeparateCall(aCallState);
return true;
}
bool
TelephonyParent::RecvHoldConference()
{
nsCOMPtr<nsITelephonyProvider> provider =
do_GetService(TELEPHONY_PROVIDER_CONTRACTID);
NS_ENSURE_TRUE(provider, true);
provider->HoldConference();
return true;
}
bool
TelephonyParent::RecvResumeConference()
{
nsCOMPtr<nsITelephonyProvider> provider =
do_GetService(TELEPHONY_PROVIDER_CONTRACTID);
NS_ENSURE_TRUE(provider, true);
provider->ResumeConference();
return true;
}
bool
TelephonyParent::RecvStartTone(const nsString& aTone)
{
nsCOMPtr<nsITelephonyProvider> provider =
do_GetService(TELEPHONY_PROVIDER_CONTRACTID);
NS_ENSURE_TRUE(provider, true);
provider->StartTone(aTone);
return true;
}
bool
TelephonyParent::RecvStopTone()
{
nsCOMPtr<nsITelephonyProvider> provider =
do_GetService(TELEPHONY_PROVIDER_CONTRACTID);
NS_ENSURE_TRUE(provider, true);
provider->StopTone();
return true;
}
bool
TelephonyParent::RecvGetMicrophoneMuted(bool* aMuted)
{
*aMuted = false;
nsCOMPtr<nsITelephonyProvider> provider =
do_GetService(TELEPHONY_PROVIDER_CONTRACTID);
NS_ENSURE_TRUE(provider, true);
provider->GetMicrophoneMuted(aMuted);
return true;
}
bool
TelephonyParent::RecvSetMicrophoneMuted(const bool& aMuted)
{
nsCOMPtr<nsITelephonyProvider> provider =
do_GetService(TELEPHONY_PROVIDER_CONTRACTID);
NS_ENSURE_TRUE(provider, true);
provider->SetMicrophoneMuted(aMuted);
return true;
}
bool
TelephonyParent::RecvGetSpeakerEnabled(bool* aEnabled)
{
*aEnabled = false;
nsCOMPtr<nsITelephonyProvider> provider =
do_GetService(TELEPHONY_PROVIDER_CONTRACTID);
NS_ENSURE_TRUE(provider, true);
provider->GetSpeakerEnabled(aEnabled);
return true;
}
bool
TelephonyParent::RecvSetSpeakerEnabled(const bool& aEnabled)
{
nsCOMPtr<nsITelephonyProvider> 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<nsITelephonyProvider> 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!");
}

View File

@ -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 */

View File

@ -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 */

View File

@ -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'

View File

@ -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);
};

View File

@ -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);

View File

@ -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",

View File

@ -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 \

View File

@ -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

View File

@ -153,6 +153,7 @@ public:
RendererSGX530,
RendererSGX540,
RendererTegra,
RendererAndroidEmulator,
RendererOther
};

View File

@ -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)

View File

@ -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);

View File

@ -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) \

View File

@ -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 }
};

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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;

View File

@ -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 */ \

View File

@ -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({});
},

View File

@ -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 \

View File

@ -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 <stdint.h>
/*
* 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

View File

@ -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 <stdint.h>
#include <sys/types.h>
#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

View File

@ -17,14 +17,15 @@
#define LOG_TAG "EventHub"
// #define LOG_NDEBUG 0
#include "cutils_log.h"
#include "utils_Log.h"
#include "EventHub.h"
#include <hardware_legacy/power.h>
#include <cutils/properties.h>
#include "Timers.h"
#include "cutils_log.h"
#include <utils/Timers.h>
#include <utils/threads.h>
#include <utils/Errors.h>
@ -48,6 +49,7 @@
#include <sys/epoll.h>
#include <sys/ioctl.h>
#include <sys/limits.h>
#include <sha1.h>
/* 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<const u_char*>(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<int32_t> 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<KeyCharacterMap> 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<KeyCharacterMap> 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<KeyCharacterMap>& 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 = "<virtual>";
setDescriptor(identifier);
Device* device = new Device(-1, VIRTUAL_KEYBOARD_ID, String8("<virtual>"), 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
}

View File

@ -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 <utils/String8.h>
#include <utils/threads.h>
#include "cutils_log.h"
#include <utils/threads.h>
#include <utils/List.h>
#include <utils/Errors.h>
#include "PropertyMap.h"
#include <utils/PropertyMap.h>
#include <utils/Vector.h>
#include <utils/KeyedVector.h>
@ -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<VirtualKeyDefinition>& outVirtualKeys) const = 0;
virtual String8 getKeyCharacterMapFile(int32_t deviceId) const = 0;
virtual sp<KeyCharacterMap> getKeyCharacterMap(int32_t deviceId) const = 0;
virtual bool setKeyboardLayoutOverlay(int32_t deviceId, const sp<KeyCharacterMap>& 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<String8>& devices);
@ -266,7 +297,11 @@ public:
virtual void getVirtualKeyDefinitions(int32_t deviceId,
Vector<VirtualKeyDefinition>& outVirtualKeys) const;
virtual String8 getKeyCharacterMapFile(int32_t deviceId) const;
virtual sp<KeyCharacterMap> getKeyCharacterMap(int32_t deviceId) const;
virtual bool setKeyboardLayoutOverlay(int32_t deviceId, const sp<KeyCharacterMap>& 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<KeyCharacterMap> overlayKeyMap;
sp<KeyCharacterMap> 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<KeyCharacterMap>& 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

View File

@ -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 <stdlib.h>
#include <unistd.h>
#include <ctype.h>
#include "utils_Log.h"
#include "Input.h"
#include "cutils_log.h"
#include <math.h>
#include <limits.h>
#include "Input.h"
#ifdef HAVE_ANDROID_OS
#include <binder/Parcel.h>
@ -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<KeyEvent*>(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<MotionEvent*>(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 &range;
}
}
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

View File

@ -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 <utils/Vector.h>
#include <utils/KeyedVector.h>
#include "Timers.h"
#include <utils/Timers.h>
#include <utils/RefBase.h>
#include "String8.h"
#include "BitSet.h"
#include <utils/String8.h>
#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<KeyEvent*> mKeyEventPool;
Vector<MotionEvent*> 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<MotionRange>& getMotionRanges() const {
return mMotionRanges;
}
private:
int32_t mId;
String8 mName;
uint32_t mSources;
int32_t mKeyboardType;
String8 mKeyCharacterMapFile;
Vector<MotionRange> 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

View File

@ -20,8 +20,8 @@
#include "Input.h"
#include <utils/RefBase.h>
#include "Timers.h"
#include "String8.h"
#include <utils/Timers.h>
#include <utils/String8.h>
namespace android {

View File

@ -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 <stdlib.h>
#include <unistd.h>
#include <ctype.h>
#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 &range;
}
}
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

View File

@ -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<KeyCharacterMap>& value) {
mKeyCharacterMap = value;
}
inline sp<KeyCharacterMap> getKeyCharacterMap() const {
return mKeyCharacterMap;
}
inline void setVibrator(bool hasVibrator) { mHasVibrator = hasVibrator; }
inline bool hasVibrator() const { return mHasVibrator; }
inline const Vector<MotionRange>& getMotionRanges() const {
return mMotionRanges;
}
private:
int32_t mId;
int32_t mGeneration;
InputDeviceIdentifier mIdentifier;
String8 mAlias;
bool mIsExternal;
uint32_t mSources;
int32_t mKeyboardType;
sp<KeyCharacterMap> mKeyCharacterMap;
bool mHasVibrator;
Vector<MotionRange> 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

File diff suppressed because it is too large Load Diff

View File

@ -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 <utils/KeyedVector.h>
#include <utils/Vector.h>
#include <utils/threads.h>
#include "Timers.h"
#include <utils/Timers.h>
#include <utils/RefBase.h>
#include "String8.h"
#include <utils/String8.h>
#include <utils/Looper.h>
#include "BitSet.h"
#include <utils/BitSet.h>
#include <cutils/atomic.h>
#include <stddef.h>
#include <unistd.h>
@ -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<DispatchEntry> {
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> inputApplicationHandle;
sp<InputWindowHandle> 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<DispatchEntry> 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<DispatchEntry> waitQueue;
explicit Connection(const sp<InputChannel>& inputChannel,
const sp<InputWindowHandle>& 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<Looper> mLooper;
EventEntry* mPendingEvent;
Queue<EventEntry> mInboundQueue;
Queue<CommandEntry> mCommandQueue;
Vector<EventEntry*> 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<InputWindowHandle> findTouchedWindowAtLocked(int32_t x, int32_t y);
sp<InputWindowHandle> findTouchedWindowAtLocked(int32_t displayId, int32_t x, int32_t y);
// All registered connections mapped by receive pipe file descriptor.
KeyedVector<int, sp<Connection> > mConnectionsByReceiveFd;
// All registered connections mapped by channel file descriptor.
KeyedVector<int, sp<Connection> > mConnectionsByFd;
ssize_t getConnectionIndexLocked(const sp<InputChannel>& 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<Connection*> mActiveConnections;
// Input channels that will receive a copy of all input events.
Vector<sp<InputChannel> > 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<TouchedWindow> windows;
TouchState();
@ -987,6 +947,7 @@ private:
void copyFrom(const TouchState& other);
void addOrUpdateWindow(const sp<InputWindowHandle>& windowHandle,
int32_t targetFlags, BitSet32 pointerIds);
void removeWindow(const sp<InputWindowHandle>& windowHandle);
void filterNonAsIsTouchWindows();
sp<InputWindowHandle> getFirstForegroundWindowHandle() const;
bool isSlippery() const;
@ -998,6 +959,9 @@ private:
// Focused application.
sp<InputApplicationHandle> 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<InputTarget>& 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<InputTarget> 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<InputWindowHandle> mLastHoverWindowHandle;
// Finding targets for input events.
void resetTargetsLocked();
void commitTargetsLocked();
int32_t handleTargetsNotReadyLocked(nsecs_t currentTime, const EventEntry* entry,
const sp<InputApplicationHandle>& applicationHandle,
const sp<InputWindowHandle>& windowHandle,
nsecs_t* nextWakeupTime);
nsecs_t* nextWakeupTime, const char* reason);
void resumeAfterTargetsNotReadyTimeoutLocked(nsecs_t newTimeout,
const sp<InputChannel>& inputChannel);
nsecs_t getTimeSpentWaitingForApplicationLocked(nsecs_t currentTime);
void resetANRTimeoutsLocked();
int32_t findFocusedWindowTargetsLocked(nsecs_t currentTime, const EventEntry* entry,
nsecs_t* nextWakeupTime);
Vector<InputTarget>& inputTargets, nsecs_t* nextWakeupTime);
int32_t findTouchedWindowTargetsLocked(nsecs_t currentTime, const MotionEntry* entry,
nsecs_t* nextWakeupTime, bool* outConflictingPointerActions,
const MotionSample** outSplitBatchAfterSample);
Vector<InputTarget>& inputTargets, nsecs_t* nextWakeupTime,
bool* outConflictingPointerActions);
void addWindowTargetLocked(const sp<InputWindowHandle>& windowHandle,
int32_t targetFlags, BitSet32 pointerIds);
void addMonitoringTargetsLocked();
int32_t targetFlags, BitSet32 pointerIds, Vector<InputTarget>& inputTargets);
void addMonitoringTargetsLocked(Vector<InputTarget>& inputTargets);
void pokeUserActivityLocked(const EventEntry* eventEntry);
bool checkInjectionPermission(const sp<InputWindowHandle>& windowHandle,
const InjectionState* injectionState);
bool isWindowObscuredAtPointLocked(const sp<InputWindowHandle>& windowHandle,
int32_t x, int32_t y) const;
bool isWindowFinishedWithPreviousInputLocked(const sp<InputWindowHandle>& windowHandle);
bool isWindowReadyForMoreInputLocked(nsecs_t currentTime,
const sp<InputWindowHandle>& windowHandle, const EventEntry* eventEntry);
String8 getApplicationWindowLabelLocked(const sp<InputApplicationHandle>& applicationHandle,
const sp<InputWindowHandle>& 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>& connection,
EventEntry* eventEntry, const InputTarget* inputTarget,
bool resumeWithAppendedMotionSample);
EventEntry* eventEntry, const InputTarget* inputTarget);
void enqueueDispatchEntriesLocked(nsecs_t currentTime, const sp<Connection>& connection,
EventEntry* eventEntry, const InputTarget* inputTarget);
void enqueueDispatchEntryLocked(const sp<Connection>& 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>& connection);
void finishDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection,
bool handled);
void startNextDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection);
uint32_t seq, bool handled);
void abortBrokenDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection,
bool notify);
void drainOutboundQueueLocked(Connection* connection);
static int handleReceiveCallback(int receiveFd, int events, void* data);
void drainDispatchQueueLocked(Queue<DispatchEntry>* 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>& connection);
void onDispatchCycleFinishedLocked(
nsecs_t currentTime, const sp<Connection>& connection, bool handled);
nsecs_t currentTime, const sp<Connection>& connection, uint32_t seq, bool handled);
void onDispatchCycleBrokenLocked(
nsecs_t currentTime, const sp<Connection>& connection);
void onANRLocked(
nsecs_t currentTime, const sp<InputApplicationHandle>& applicationHandle,
const sp<InputWindowHandle>& 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>& connection);
void traceWaitQueueLengthLocked(const sp<Connection>& connection);
};
/* Enqueues and dispatches input events, endlessly. */

View File

@ -69,12 +69,12 @@ void NotifyKeyArgs::notify(const sp<InputListenerInterface>& 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<InputListenerInterface>& 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<InputListenerInterface>& listener) const {

View File

@ -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);

View File

@ -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<EventHubInterface>& eventHub,
const sp<InputReaderPolicyInterface>& readerPolicy,
const sp<InputDispatcherPolicyInterface>& dispatcherPolicy) {
mDispatcher = new InputDispatcher(dispatcherPolicy);
mReader = new InputReader(eventHub, readerPolicy, mDispatcher);
initialize();
}
InputManager::InputManager(
const sp<InputReaderInterface>& reader,
const sp<InputDispatcherInterface>& 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<InputReaderInterface> InputManager::getReader() {
return mReader;
}
sp<InputDispatcherInterface> InputManager::getDispatcher() {
return mDispatcher;
}
} // namespace android

View File

@ -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 <utils/Errors.h>
#include <utils/Vector.h>
#include <utils/Timers.h>
#include <utils/RefBase.h>
#include <utils/String8.h>
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<InputReaderInterface> getReader() = 0;
/* Gets the input dispatcher. */
virtual sp<InputDispatcherInterface> getDispatcher() = 0;
};
class InputManager : public InputManagerInterface {
protected:
virtual ~InputManager();
public:
InputManager(
const sp<EventHubInterface>& eventHub,
const sp<InputReaderPolicyInterface>& readerPolicy,
const sp<InputDispatcherPolicyInterface>& dispatcherPolicy);
// (used for testing purposes)
InputManager(
const sp<InputReaderInterface>& reader,
const sp<InputDispatcherInterface>& dispatcher);
virtual status_t start();
virtual status_t stop();
virtual sp<InputReaderInterface> getReader();
virtual sp<InputDispatcherInterface> getDispatcher();
private:
sp<InputReaderInterface> mReader;
sp<InputReaderThread> mReaderThread;
sp<InputDispatcherInterface> mDispatcher;
sp<InputDispatcherThread> mDispatcherThread;
void initialize();
};
} // namespace android
#endif // _UI_INPUT_MANAGER_H

File diff suppressed because it is too large Load Diff

View File

@ -22,22 +22,94 @@
#include "InputListener.h"
#include "Input.h"
#include "DisplayInfo.h"
#include "VelocityControl.h"
#include "VelocityTracker.h"
#include <utils/KeyedVector.h>
#include <utils/threads.h>
#include "Timers.h"
#include <utils/Timers.h>
#include <utils/RefBase.h>
#include "String8.h"
#include "BitSet.h"
#include <utils/String8.h>
#include <utils/BitSet.h>
#include <stddef.h>
#include <unistd.h>
// 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<PointerControllerInterface> 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<InputDeviceInfo>& inputDevices) = 0;
/* Gets the keyboard layout for a particular input device. */
virtual sp<KeyCharacterMap> 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<int32_t>& outDeviceIds) = 0;
virtual void getInputDevices(Vector<InputDeviceInfo>& 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<int32_t>& outDeviceIds);
virtual void getInputDevices(Vector<InputDeviceInfo>& 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<EventHubInterface> mEventHub;
sp<InputReaderPolicyInterface> mPolicy;
sp<QueuedInputListener> mQueuedListener;
@ -391,8 +466,10 @@ private:
void fadePointerLocked();
InputConfiguration mInputConfiguration;
void updateInputConfigurationLocked();
int32_t mGeneration;
int32_t bumpGenerationLocked();
void getInputDevicesLocked(Vector<InputDeviceInfo>& 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<InputMapper*> 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

File diff suppressed because it is too large Load Diff

View File

@ -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 <semaphore.h>
#include "Input.h"
#include <utils/Errors.h>
#include "Timers.h"
#include <utils/Timers.h>
#include <utils/RefBase.h>
#include "String8.h"
#include <utils/String8.h>
#include <utils/Vector.h>
#include <utils/BitSet.h>
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<InputChannel>& outServerChannel, sp<InputChannel>& 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<InputMessage::SampleData*>(reinterpret_cast<char*>(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<InputChannel>& outServerChannel, sp<InputChannel>& 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<InputChannel> 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<InputChannel> 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<InputChannel> 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<InputChannel> 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<InputChannel> 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<InputMessage> samples;
};
Vector<Batch> 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<TouchState> 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<SeqChain> 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

View File

@ -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 {

View File

@ -20,10 +20,10 @@
#include "Input.h"
#include "InputTransport.h"
#include <utils/RefBase.h>
#include "Timers.h"
#include "String8.h"
#include <utils/Timers.h>
#include <utils/String8.h>
#include "SkRegion.h"
#include <SkRegion.h>
#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> 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;

View File

@ -15,16 +15,21 @@
*/
#define LOG_TAG "KeyCharacterMap"
#include "cutils_log.h"
#include <stdlib.h>
#include <string.h>
#include "utils_Log.h"
#include "android_keycodes.h"
#include "Keyboard.h"
#include "KeyCharacterMap.h"
#if HAVE_ANDROID_OS
#include <binder/Parcel.h>
#endif
#include <utils/Errors.h>
#include "Tokenizer.h"
#include "Timers.h"
#include <utils/Timers.h>
// 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> 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<KeyCharacterMap>* 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<KeyCharacterMap>* 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<KeyCharacterMap>* outMap) {
status_t status = OK;
sp<KeyCharacterMap> 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> KeyCharacterMap::combine(const sp<KeyCharacterMap>& base,
const sp<KeyCharacterMap>& overlay) {
if (overlay == NULL) {
return base;
}
if (base == NULL) {
return overlay;
}
sp<KeyCharacterMap> 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> 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<KeyEvent>& outEvents,
}
}
#if HAVE_ANDROID_OS
sp<KeyCharacterMap> KeyCharacterMap::readFromParcel(Parcel* parcel) {
sp<KeyCharacterMap> 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<int32_t, int32_t>& 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<Property> 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;

Some files were not shown because too many files have changed in this diff Show More