1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/widget/gonk/nsAppShell.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1124 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 1.5 +/* vim: set ts=4 sw=4 sts=4 tw=80 et: */ 1.6 +/* Copyright 2012 Mozilla Foundation and Mozilla contributors 1.7 + * 1.8 + * Licensed under the Apache License, Version 2.0 (the "License"); 1.9 + * you may not use this file except in compliance with the License. 1.10 + * You may obtain a copy of the License at 1.11 + * 1.12 + * http://www.apache.org/licenses/LICENSE-2.0 1.13 + * 1.14 + * Unless required by applicable law or agreed to in writing, software 1.15 + * distributed under the License is distributed on an "AS IS" BASIS, 1.16 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1.17 + * See the License for the specific language governing permissions and 1.18 + * limitations under the License. 1.19 + */ 1.20 + 1.21 +#ifndef _GNU_SOURCE 1.22 +#define _GNU_SOURCE 1.23 +#endif 1.24 + 1.25 +#include <dirent.h> 1.26 +#include <errno.h> 1.27 +#include <fcntl.h> 1.28 +#include <signal.h> 1.29 +#include <sys/epoll.h> 1.30 +#include <sys/ioctl.h> 1.31 +#include <sys/param.h> 1.32 +#include <sys/stat.h> 1.33 +#include <sys/types.h> 1.34 +#include <unistd.h> 1.35 +#include <utils/BitSet.h> 1.36 + 1.37 +#include "base/basictypes.h" 1.38 +#include "GonkPermission.h" 1.39 +#include "nscore.h" 1.40 +#ifdef MOZ_OMX_DECODER 1.41 +#include "MediaResourceManagerService.h" 1.42 +#endif 1.43 +#include "mozilla/TouchEvents.h" 1.44 +#include "mozilla/FileUtils.h" 1.45 +#include "mozilla/Hal.h" 1.46 +#include "mozilla/MouseEvents.h" 1.47 +#include "mozilla/Mutex.h" 1.48 +#include "mozilla/Services.h" 1.49 +#include "mozilla/TextEvents.h" 1.50 +#if ANDROID_VERSION >= 18 1.51 +#include "nativewindow/FakeSurfaceComposer.h" 1.52 +#endif 1.53 +#include "nsAppShell.h" 1.54 +#include "mozilla/dom/Touch.h" 1.55 +#include "nsGkAtoms.h" 1.56 +#include "nsIObserverService.h" 1.57 +#include "nsIScreen.h" 1.58 +#include "nsScreenManagerGonk.h" 1.59 +#include "nsThreadUtils.h" 1.60 +#include "nsWindow.h" 1.61 +#include "OrientationObserver.h" 1.62 +#include "GonkMemoryPressureMonitoring.h" 1.63 + 1.64 +#include "android/log.h" 1.65 +#include "libui/EventHub.h" 1.66 +#include "libui/InputReader.h" 1.67 +#include "libui/InputDispatcher.h" 1.68 +#include "cutils/properties.h" 1.69 + 1.70 +#ifdef MOZ_NUWA_PROCESS 1.71 +#include "ipc/Nuwa.h" 1.72 +#endif 1.73 + 1.74 +#include "GeckoProfiler.h" 1.75 + 1.76 +// Defines kKeyMapping and GetKeyNameIndex() 1.77 +#include "GonkKeyMapping.h" 1.78 + 1.79 +#define LOG(args...) \ 1.80 + __android_log_print(ANDROID_LOG_INFO, "Gonk" , ## args) 1.81 +#ifdef VERBOSE_LOG_ENABLED 1.82 +# define VERBOSE_LOG(args...) \ 1.83 + __android_log_print(ANDROID_LOG_INFO, "Gonk" , ## args) 1.84 +#else 1.85 +# define VERBOSE_LOG(args...) \ 1.86 + (void)0 1.87 +#endif 1.88 + 1.89 +using namespace android; 1.90 +using namespace mozilla; 1.91 +using namespace mozilla::dom; 1.92 +using namespace mozilla::services; 1.93 +using namespace mozilla::widget; 1.94 + 1.95 +bool gDrawRequest = false; 1.96 +static nsAppShell *gAppShell = nullptr; 1.97 +static int epollfd = 0; 1.98 +static int signalfds[2] = {0}; 1.99 +static bool sDevInputAudioJack; 1.100 +static int32_t sHeadphoneState; 1.101 +static int32_t sMicrophoneState; 1.102 + 1.103 +// Amount of time in MS before an input is considered expired. 1.104 +static const uint64_t kInputExpirationThresholdMs = 1000; 1.105 + 1.106 +NS_IMPL_ISUPPORTS_INHERITED(nsAppShell, nsBaseAppShell, nsIObserver) 1.107 + 1.108 +static uint64_t 1.109 +nanosecsToMillisecs(nsecs_t nsecs) 1.110 +{ 1.111 + return nsecs / 1000000; 1.112 +} 1.113 + 1.114 +namespace mozilla { 1.115 + 1.116 +bool ProcessNextEvent() 1.117 +{ 1.118 + return gAppShell->ProcessNextNativeEvent(true); 1.119 +} 1.120 + 1.121 +void NotifyEvent() 1.122 +{ 1.123 + gAppShell->NotifyNativeEvent(); 1.124 +} 1.125 + 1.126 +} // namespace mozilla 1.127 + 1.128 +static void 1.129 +pipeHandler(int fd, FdHandler *data) 1.130 +{ 1.131 + ssize_t len; 1.132 + do { 1.133 + char tmp[32]; 1.134 + len = read(fd, tmp, sizeof(tmp)); 1.135 + } while (len > 0); 1.136 +} 1.137 + 1.138 +struct Touch { 1.139 + int32_t id; 1.140 + PointerCoords coords; 1.141 +}; 1.142 + 1.143 +struct UserInputData { 1.144 + uint64_t timeMs; 1.145 + enum { 1.146 + MOTION_DATA, 1.147 + KEY_DATA 1.148 + } type; 1.149 + int32_t action; 1.150 + int32_t flags; 1.151 + int32_t metaState; 1.152 + int32_t deviceId; 1.153 + union { 1.154 + struct { 1.155 + int32_t keyCode; 1.156 + int32_t scanCode; 1.157 + } key; 1.158 + struct { 1.159 + int32_t touchCount; 1.160 + ::Touch touches[MAX_POINTERS]; 1.161 + } motion; 1.162 + }; 1.163 + 1.164 + Modifiers DOMModifiers() const; 1.165 +}; 1.166 + 1.167 +Modifiers 1.168 +UserInputData::DOMModifiers() const 1.169 +{ 1.170 + Modifiers result = 0; 1.171 + if (metaState & (AMETA_ALT_ON | AMETA_ALT_LEFT_ON | AMETA_ALT_RIGHT_ON)) { 1.172 + result |= MODIFIER_ALT; 1.173 + } 1.174 + if (metaState & (AMETA_SHIFT_ON | 1.175 + AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_RIGHT_ON)) { 1.176 + result |= MODIFIER_SHIFT; 1.177 + } 1.178 + if (metaState & AMETA_FUNCTION_ON) { 1.179 + result |= MODIFIER_FN; 1.180 + } 1.181 + if (metaState & (AMETA_CTRL_ON | 1.182 + AMETA_CTRL_LEFT_ON | AMETA_CTRL_RIGHT_ON)) { 1.183 + result |= MODIFIER_CONTROL; 1.184 + } 1.185 + if (metaState & (AMETA_META_ON | 1.186 + AMETA_META_LEFT_ON | AMETA_META_RIGHT_ON)) { 1.187 + result |= MODIFIER_META; 1.188 + } 1.189 + if (metaState & AMETA_CAPS_LOCK_ON) { 1.190 + result |= MODIFIER_CAPSLOCK; 1.191 + } 1.192 + if (metaState & AMETA_NUM_LOCK_ON) { 1.193 + result |= MODIFIER_NUMLOCK; 1.194 + } 1.195 + if (metaState & AMETA_SCROLL_LOCK_ON) { 1.196 + result |= MODIFIER_SCROLLLOCK; 1.197 + } 1.198 + return result; 1.199 +} 1.200 + 1.201 +static void 1.202 +sendMouseEvent(uint32_t msg, UserInputData& data, bool forwardToChildren) 1.203 +{ 1.204 + WidgetMouseEvent event(true, msg, nullptr, 1.205 + WidgetMouseEvent::eReal, WidgetMouseEvent::eNormal); 1.206 + 1.207 + event.refPoint.x = data.motion.touches[0].coords.getX(); 1.208 + event.refPoint.y = data.motion.touches[0].coords.getY(); 1.209 + event.time = data.timeMs; 1.210 + event.button = WidgetMouseEvent::eLeftButton; 1.211 + event.inputSource = nsIDOMMouseEvent::MOZ_SOURCE_TOUCH; 1.212 + if (msg != NS_MOUSE_MOVE) 1.213 + event.clickCount = 1; 1.214 + event.modifiers = data.DOMModifiers(); 1.215 + 1.216 + event.mFlags.mNoCrossProcessBoundaryForwarding = !forwardToChildren; 1.217 + 1.218 + nsWindow::DispatchInputEvent(event); 1.219 +} 1.220 + 1.221 +static void 1.222 +addDOMTouch(UserInputData& data, WidgetTouchEvent& event, int i) 1.223 +{ 1.224 + const ::Touch& touch = data.motion.touches[i]; 1.225 + event.touches.AppendElement( 1.226 + new dom::Touch(touch.id, 1.227 + nsIntPoint(floor(touch.coords.getX() + 0.5), floor(touch.coords.getY() + 0.5)), 1.228 + nsIntPoint(touch.coords.getAxisValue(AMOTION_EVENT_AXIS_SIZE), 1.229 + touch.coords.getAxisValue(AMOTION_EVENT_AXIS_SIZE)), 1.230 + 0, 1.231 + touch.coords.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE)) 1.232 + ); 1.233 +} 1.234 + 1.235 +static nsEventStatus 1.236 +sendTouchEvent(UserInputData& data, bool* captured) 1.237 +{ 1.238 + uint32_t msg; 1.239 + int32_t action = data.action & AMOTION_EVENT_ACTION_MASK; 1.240 + switch (action) { 1.241 + case AMOTION_EVENT_ACTION_DOWN: 1.242 + case AMOTION_EVENT_ACTION_POINTER_DOWN: 1.243 + msg = NS_TOUCH_START; 1.244 + break; 1.245 + case AMOTION_EVENT_ACTION_MOVE: 1.246 + msg = NS_TOUCH_MOVE; 1.247 + break; 1.248 + case AMOTION_EVENT_ACTION_UP: 1.249 + case AMOTION_EVENT_ACTION_POINTER_UP: 1.250 + msg = NS_TOUCH_END; 1.251 + break; 1.252 + case AMOTION_EVENT_ACTION_OUTSIDE: 1.253 + case AMOTION_EVENT_ACTION_CANCEL: 1.254 + msg = NS_TOUCH_CANCEL; 1.255 + break; 1.256 + default: 1.257 + msg = NS_EVENT_NULL; 1.258 + break; 1.259 + } 1.260 + 1.261 + WidgetTouchEvent event(true, msg, nullptr); 1.262 + 1.263 + event.time = data.timeMs; 1.264 + event.modifiers = data.DOMModifiers(); 1.265 + 1.266 + int32_t i; 1.267 + if (msg == NS_TOUCH_END) { 1.268 + i = data.action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK; 1.269 + i >>= AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT; 1.270 + addDOMTouch(data, event, i); 1.271 + } else { 1.272 + for (i = 0; i < data.motion.touchCount; ++i) 1.273 + addDOMTouch(data, event, i); 1.274 + } 1.275 + 1.276 + return nsWindow::DispatchInputEvent(event, captured); 1.277 +} 1.278 + 1.279 +class MOZ_STACK_CLASS KeyEventDispatcher 1.280 +{ 1.281 +public: 1.282 + KeyEventDispatcher(const UserInputData& aData, 1.283 + KeyCharacterMap* aKeyCharMap); 1.284 + void Dispatch(); 1.285 + 1.286 +private: 1.287 + const UserInputData& mData; 1.288 + sp<KeyCharacterMap> mKeyCharMap; 1.289 + 1.290 + char16_t mChar; 1.291 + char16_t mUnmodifiedChar; 1.292 + 1.293 + uint32_t mDOMKeyCode; 1.294 + KeyNameIndex mDOMKeyNameIndex; 1.295 + char16_t mDOMPrintableKeyValue; 1.296 + 1.297 + bool IsKeyPress() const 1.298 + { 1.299 + return mData.action == AKEY_EVENT_ACTION_DOWN; 1.300 + } 1.301 + bool IsRepeat() const 1.302 + { 1.303 + return IsKeyPress() && (mData.flags & AKEY_EVENT_FLAG_LONG_PRESS); 1.304 + } 1.305 + 1.306 + char16_t PrintableKeyValue() const; 1.307 + 1.308 + int32_t UnmodifiedMetaState() const 1.309 + { 1.310 + return mData.metaState & 1.311 + ~(AMETA_ALT_ON | AMETA_ALT_LEFT_ON | AMETA_ALT_RIGHT_ON | 1.312 + AMETA_CTRL_ON | AMETA_CTRL_LEFT_ON | AMETA_CTRL_RIGHT_ON | 1.313 + AMETA_META_ON | AMETA_META_LEFT_ON | AMETA_META_RIGHT_ON); 1.314 + } 1.315 + 1.316 + static bool IsControlChar(char16_t aChar) 1.317 + { 1.318 + return (aChar < ' ' || aChar == 0x7F); 1.319 + } 1.320 + 1.321 + void DispatchKeyDownEvent(); 1.322 + void DispatchKeyUpEvent(); 1.323 + nsEventStatus DispatchKeyEventInternal(uint32_t aEventMessage); 1.324 +}; 1.325 + 1.326 +KeyEventDispatcher::KeyEventDispatcher(const UserInputData& aData, 1.327 + KeyCharacterMap* aKeyCharMap) : 1.328 + mData(aData), mKeyCharMap(aKeyCharMap), mChar(0), mUnmodifiedChar(0), 1.329 + mDOMPrintableKeyValue(0) 1.330 +{ 1.331 + // XXX Printable key's keyCode value should be computed with actual 1.332 + // input character. 1.333 + mDOMKeyCode = (mData.key.keyCode < (ssize_t)ArrayLength(kKeyMapping)) ? 1.334 + kKeyMapping[mData.key.keyCode] : 0; 1.335 + mDOMKeyNameIndex = GetKeyNameIndex(mData.key.keyCode); 1.336 + 1.337 + if (!mKeyCharMap.get()) { 1.338 + return; 1.339 + } 1.340 + 1.341 + mChar = mKeyCharMap->getCharacter(mData.key.keyCode, mData.metaState); 1.342 + if (IsControlChar(mChar)) { 1.343 + mChar = 0; 1.344 + } 1.345 + int32_t unmodifiedMetaState = UnmodifiedMetaState(); 1.346 + if (mData.metaState == unmodifiedMetaState) { 1.347 + mUnmodifiedChar = mChar; 1.348 + } else { 1.349 + mUnmodifiedChar = mKeyCharMap->getCharacter(mData.key.keyCode, 1.350 + unmodifiedMetaState); 1.351 + if (IsControlChar(mUnmodifiedChar)) { 1.352 + mUnmodifiedChar = 0; 1.353 + } 1.354 + } 1.355 + 1.356 + mDOMPrintableKeyValue = PrintableKeyValue(); 1.357 +} 1.358 + 1.359 +char16_t 1.360 +KeyEventDispatcher::PrintableKeyValue() const 1.361 +{ 1.362 + if (mDOMKeyNameIndex != KEY_NAME_INDEX_USE_STRING) { 1.363 + return 0; 1.364 + } 1.365 + return mChar ? mChar : mUnmodifiedChar; 1.366 +} 1.367 + 1.368 +nsEventStatus 1.369 +KeyEventDispatcher::DispatchKeyEventInternal(uint32_t aEventMessage) 1.370 +{ 1.371 + WidgetKeyboardEvent event(true, aEventMessage, nullptr); 1.372 + if (aEventMessage == NS_KEY_PRESS) { 1.373 + // XXX If the charCode is not a printable character, the charCode 1.374 + // should be computed without Ctrl/Alt/Meta modifiers. 1.375 + event.charCode = static_cast<uint32_t>(mChar); 1.376 + } 1.377 + if (!event.charCode) { 1.378 + event.keyCode = mDOMKeyCode; 1.379 + } 1.380 + event.isChar = !!event.charCode; 1.381 + event.mIsRepeat = IsRepeat(); 1.382 + event.mKeyNameIndex = mDOMKeyNameIndex; 1.383 + if (mDOMPrintableKeyValue) { 1.384 + event.mKeyValue = mDOMPrintableKeyValue; 1.385 + } 1.386 + event.modifiers = mData.DOMModifiers(); 1.387 + event.location = nsIDOMKeyEvent::DOM_KEY_LOCATION_MOBILE; 1.388 + event.time = mData.timeMs; 1.389 + return nsWindow::DispatchInputEvent(event); 1.390 +} 1.391 + 1.392 +void 1.393 +KeyEventDispatcher::Dispatch() 1.394 +{ 1.395 + // XXX Even if unknown key is pressed, DOM key event should be 1.396 + // dispatched since Gecko for the other platforms are implemented 1.397 + // as so. 1.398 + if (!mDOMKeyCode && mDOMKeyNameIndex == KEY_NAME_INDEX_Unidentified) { 1.399 + VERBOSE_LOG("Got unknown key event code. " 1.400 + "type 0x%04x code 0x%04x value %d", 1.401 + mData.action, mData.key.keyCode, IsKeyPress()); 1.402 + return; 1.403 + } 1.404 + 1.405 + if (IsKeyPress()) { 1.406 + DispatchKeyDownEvent(); 1.407 + } else { 1.408 + DispatchKeyUpEvent(); 1.409 + } 1.410 +} 1.411 + 1.412 +void 1.413 +KeyEventDispatcher::DispatchKeyDownEvent() 1.414 +{ 1.415 + nsEventStatus status = DispatchKeyEventInternal(NS_KEY_DOWN); 1.416 + if (status != nsEventStatus_eConsumeNoDefault) { 1.417 + DispatchKeyEventInternal(NS_KEY_PRESS); 1.418 + } 1.419 +} 1.420 + 1.421 +void 1.422 +KeyEventDispatcher::DispatchKeyUpEvent() 1.423 +{ 1.424 + DispatchKeyEventInternal(NS_KEY_UP); 1.425 +} 1.426 + 1.427 +class SwitchEventRunnable : public nsRunnable { 1.428 +public: 1.429 + SwitchEventRunnable(hal::SwitchEvent& aEvent) : mEvent(aEvent) 1.430 + {} 1.431 + 1.432 + NS_IMETHOD Run() 1.433 + { 1.434 + hal::NotifySwitchStateFromInputDevice(mEvent.device(), 1.435 + mEvent.status()); 1.436 + return NS_OK; 1.437 + } 1.438 +private: 1.439 + hal::SwitchEvent mEvent; 1.440 +}; 1.441 + 1.442 +static void 1.443 +updateHeadphoneSwitch() 1.444 +{ 1.445 + hal::SwitchEvent event; 1.446 + 1.447 + switch (sHeadphoneState) { 1.448 + case AKEY_STATE_UP: 1.449 + event.status() = hal::SWITCH_STATE_OFF; 1.450 + break; 1.451 + case AKEY_STATE_DOWN: 1.452 + event.status() = sMicrophoneState == AKEY_STATE_DOWN ? 1.453 + hal::SWITCH_STATE_HEADSET : hal::SWITCH_STATE_HEADPHONE; 1.454 + break; 1.455 + default: 1.456 + return; 1.457 + } 1.458 + 1.459 + event.device() = hal::SWITCH_HEADPHONES; 1.460 + NS_DispatchToMainThread(new SwitchEventRunnable(event)); 1.461 +} 1.462 + 1.463 +class GeckoPointerController : public PointerControllerInterface { 1.464 + float mX; 1.465 + float mY; 1.466 + int32_t mButtonState; 1.467 + InputReaderConfiguration* mConfig; 1.468 +public: 1.469 + GeckoPointerController(InputReaderConfiguration* config) 1.470 + : mX(0) 1.471 + , mY(0) 1.472 + , mButtonState(0) 1.473 + , mConfig(config) 1.474 + {} 1.475 + 1.476 + virtual bool getBounds(float* outMinX, float* outMinY, 1.477 + float* outMaxX, float* outMaxY) const; 1.478 + virtual void move(float deltaX, float deltaY); 1.479 + virtual void setButtonState(int32_t buttonState); 1.480 + virtual int32_t getButtonState() const; 1.481 + virtual void setPosition(float x, float y); 1.482 + virtual void getPosition(float* outX, float* outY) const; 1.483 + virtual void fade(Transition transition) {} 1.484 + virtual void unfade(Transition transition) {} 1.485 + virtual void setPresentation(Presentation presentation) {} 1.486 + virtual void setSpots(const PointerCoords* spotCoords, const uint32_t* spotIdToIndex, 1.487 + BitSet32 spotIdBits) {} 1.488 + virtual void clearSpots() {} 1.489 +}; 1.490 + 1.491 +bool 1.492 +GeckoPointerController::getBounds(float* outMinX, 1.493 + float* outMinY, 1.494 + float* outMaxX, 1.495 + float* outMaxY) const 1.496 +{ 1.497 + DisplayViewport viewport; 1.498 + 1.499 + mConfig->getDisplayInfo(false, &viewport); 1.500 + 1.501 + *outMinX = *outMinY = 0; 1.502 + *outMaxX = viewport.logicalRight; 1.503 + *outMaxY = viewport.logicalBottom; 1.504 + return true; 1.505 +} 1.506 + 1.507 +void 1.508 +GeckoPointerController::move(float deltaX, float deltaY) 1.509 +{ 1.510 + float minX, minY, maxX, maxY; 1.511 + getBounds(&minX, &minY, &maxX, &maxY); 1.512 + 1.513 + mX = clamped(mX + deltaX, minX, maxX); 1.514 + mY = clamped(mY + deltaY, minY, maxY); 1.515 +} 1.516 + 1.517 +void 1.518 +GeckoPointerController::setButtonState(int32_t buttonState) 1.519 +{ 1.520 + mButtonState = buttonState; 1.521 +} 1.522 + 1.523 +int32_t 1.524 +GeckoPointerController::getButtonState() const 1.525 +{ 1.526 + return mButtonState; 1.527 +} 1.528 + 1.529 +void 1.530 +GeckoPointerController::setPosition(float x, float y) 1.531 +{ 1.532 + mX = x; 1.533 + mY = y; 1.534 +} 1.535 + 1.536 +void 1.537 +GeckoPointerController::getPosition(float* outX, float* outY) const 1.538 +{ 1.539 + *outX = mX; 1.540 + *outY = mY; 1.541 +} 1.542 + 1.543 +class GeckoInputReaderPolicy : public InputReaderPolicyInterface { 1.544 + InputReaderConfiguration mConfig; 1.545 +public: 1.546 + GeckoInputReaderPolicy() {} 1.547 + 1.548 + virtual void getReaderConfiguration(InputReaderConfiguration* outConfig); 1.549 + virtual sp<PointerControllerInterface> obtainPointerController(int32_t 1.550 +deviceId) 1.551 + { 1.552 + return new GeckoPointerController(&mConfig); 1.553 + }; 1.554 + virtual void notifyInputDevicesChanged(const android::Vector<InputDeviceInfo>& inputDevices) {}; 1.555 + virtual sp<KeyCharacterMap> getKeyboardLayoutOverlay(const String8& inputDeviceDescriptor) 1.556 + { 1.557 + return nullptr; 1.558 + }; 1.559 + virtual String8 getDeviceAlias(const InputDeviceIdentifier& identifier) 1.560 + { 1.561 + return String8::empty(); 1.562 + }; 1.563 + 1.564 + void setDisplayInfo(); 1.565 + 1.566 +protected: 1.567 + virtual ~GeckoInputReaderPolicy() {} 1.568 +}; 1.569 + 1.570 +class GeckoInputDispatcher : public InputDispatcherInterface { 1.571 +public: 1.572 + GeckoInputDispatcher(sp<EventHub> &aEventHub) 1.573 + : mQueueLock("GeckoInputDispatcher::mQueueMutex") 1.574 + , mEventHub(aEventHub) 1.575 + , mTouchDownCount(0) 1.576 + , mKeyDownCount(0) 1.577 + , mTouchEventsFiltered(false) 1.578 + , mKeyEventsFiltered(false) 1.579 + {} 1.580 + 1.581 + virtual void dump(String8& dump); 1.582 + 1.583 + virtual void monitor() {} 1.584 + 1.585 + // Called on the main thread 1.586 + virtual void dispatchOnce(); 1.587 + 1.588 + // notify* methods are called on the InputReaderThread 1.589 + virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args); 1.590 + virtual void notifyKey(const NotifyKeyArgs* args); 1.591 + virtual void notifyMotion(const NotifyMotionArgs* args); 1.592 + virtual void notifySwitch(const NotifySwitchArgs* args); 1.593 + virtual void notifyDeviceReset(const NotifyDeviceResetArgs* args); 1.594 + 1.595 + virtual int32_t injectInputEvent(const InputEvent* event, 1.596 + int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis, 1.597 + uint32_t policyFlags); 1.598 + 1.599 + virtual void setInputWindows(const android::Vector<sp<InputWindowHandle> >& inputWindowHandles); 1.600 + virtual void setFocusedApplication(const sp<InputApplicationHandle>& inputApplicationHandle); 1.601 + 1.602 + virtual void setInputDispatchMode(bool enabled, bool frozen); 1.603 + virtual void setInputFilterEnabled(bool enabled) {} 1.604 + virtual bool transferTouchFocus(const sp<InputChannel>& fromChannel, 1.605 + const sp<InputChannel>& toChannel) { return true; } 1.606 + 1.607 + virtual status_t registerInputChannel(const sp<InputChannel>& inputChannel, 1.608 + const sp<InputWindowHandle>& inputWindowHandle, bool monitor); 1.609 + virtual status_t unregisterInputChannel(const sp<InputChannel>& inputChannel); 1.610 + 1.611 + 1.612 + 1.613 +protected: 1.614 + virtual ~GeckoInputDispatcher() {} 1.615 + 1.616 +private: 1.617 + // mQueueLock should generally be locked while using mEventQueue. 1.618 + // UserInputData is pushed on on the InputReaderThread and 1.619 + // popped and dispatched on the main thread. 1.620 + mozilla::Mutex mQueueLock; 1.621 + std::queue<UserInputData> mEventQueue; 1.622 + sp<EventHub> mEventHub; 1.623 + 1.624 + int mTouchDownCount; 1.625 + int mKeyDownCount; 1.626 + bool mTouchEventsFiltered; 1.627 + bool mKeyEventsFiltered; 1.628 + BitSet32 mTouchDown; 1.629 +}; 1.630 + 1.631 +// GeckoInputReaderPolicy 1.632 +void 1.633 +GeckoInputReaderPolicy::setDisplayInfo() 1.634 +{ 1.635 + static_assert(nsIScreen::ROTATION_0_DEG == 1.636 + DISPLAY_ORIENTATION_0, 1.637 + "Orientation enums not matched!"); 1.638 + static_assert(nsIScreen::ROTATION_90_DEG == 1.639 + DISPLAY_ORIENTATION_90, 1.640 + "Orientation enums not matched!"); 1.641 + static_assert(nsIScreen::ROTATION_180_DEG == 1.642 + DISPLAY_ORIENTATION_180, 1.643 + "Orientation enums not matched!"); 1.644 + static_assert(nsIScreen::ROTATION_270_DEG == 1.645 + DISPLAY_ORIENTATION_270, 1.646 + "Orientation enums not matched!"); 1.647 + 1.648 + DisplayViewport viewport; 1.649 + viewport.displayId = 0; 1.650 + viewport.orientation = nsScreenGonk::GetRotation(); 1.651 + viewport.physicalRight = viewport.deviceWidth = gScreenBounds.width; 1.652 + viewport.physicalBottom = viewport.deviceHeight = gScreenBounds.height; 1.653 + if (viewport.orientation == DISPLAY_ORIENTATION_90 || 1.654 + viewport.orientation == DISPLAY_ORIENTATION_270) { 1.655 + viewport.logicalRight = gScreenBounds.height; 1.656 + viewport.logicalBottom = gScreenBounds.width; 1.657 + } else { 1.658 + viewport.logicalRight = gScreenBounds.width; 1.659 + viewport.logicalBottom = gScreenBounds.height; 1.660 + } 1.661 + mConfig.setDisplayInfo(false, viewport); 1.662 +} 1.663 + 1.664 +void GeckoInputReaderPolicy::getReaderConfiguration(InputReaderConfiguration* outConfig) 1.665 +{ 1.666 + *outConfig = mConfig; 1.667 +} 1.668 + 1.669 + 1.670 +// GeckoInputDispatcher 1.671 +void 1.672 +GeckoInputDispatcher::dump(String8& dump) 1.673 +{ 1.674 +} 1.675 + 1.676 +static bool 1.677 +isExpired(const UserInputData& data) 1.678 +{ 1.679 + uint64_t timeNowMs = 1.680 + nanosecsToMillisecs(systemTime(SYSTEM_TIME_MONOTONIC)); 1.681 + return (timeNowMs - data.timeMs) > kInputExpirationThresholdMs; 1.682 +} 1.683 + 1.684 +void 1.685 +GeckoInputDispatcher::dispatchOnce() 1.686 +{ 1.687 + UserInputData data; 1.688 + { 1.689 + MutexAutoLock lock(mQueueLock); 1.690 + if (mEventQueue.empty()) 1.691 + return; 1.692 + data = mEventQueue.front(); 1.693 + mEventQueue.pop(); 1.694 + if (!mEventQueue.empty()) 1.695 + gAppShell->NotifyNativeEvent(); 1.696 + } 1.697 + 1.698 + switch (data.type) { 1.699 + case UserInputData::MOTION_DATA: { 1.700 + if (!mTouchDownCount) { 1.701 + // No pending events, the filter state can be updated. 1.702 + mTouchEventsFiltered = isExpired(data); 1.703 + } 1.704 + 1.705 + int32_t action = data.action & AMOTION_EVENT_ACTION_MASK; 1.706 + int32_t index = data.action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK; 1.707 + index >>= AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT; 1.708 + switch (action) { 1.709 + case AMOTION_EVENT_ACTION_DOWN: 1.710 + case AMOTION_EVENT_ACTION_POINTER_DOWN: 1.711 + if (!mTouchDown.hasBit(index)) { 1.712 + mTouchDown.markBit(index); 1.713 + mTouchDownCount++; 1.714 + } 1.715 + break; 1.716 + case AMOTION_EVENT_ACTION_MOVE: 1.717 + case AMOTION_EVENT_ACTION_HOVER_MOVE: 1.718 + // No need to update the count on move. 1.719 + break; 1.720 + case AMOTION_EVENT_ACTION_UP: 1.721 + case AMOTION_EVENT_ACTION_POINTER_UP: 1.722 + case AMOTION_EVENT_ACTION_OUTSIDE: 1.723 + case AMOTION_EVENT_ACTION_CANCEL: 1.724 + if (mTouchDown.hasBit(index)) { 1.725 + mTouchDown.clearBit(index); 1.726 + mTouchDownCount--; 1.727 + } 1.728 + break; 1.729 + default: 1.730 + break; 1.731 + } 1.732 + 1.733 + if (mTouchEventsFiltered) { 1.734 + return; 1.735 + } 1.736 + 1.737 + nsEventStatus status = nsEventStatus_eIgnore; 1.738 + if (action != AMOTION_EVENT_ACTION_HOVER_MOVE) { 1.739 + bool captured; 1.740 + status = sendTouchEvent(data, &captured); 1.741 + if (captured) { 1.742 + return; 1.743 + } 1.744 + } 1.745 + 1.746 + uint32_t msg; 1.747 + switch (action) { 1.748 + case AMOTION_EVENT_ACTION_DOWN: 1.749 + msg = NS_MOUSE_BUTTON_DOWN; 1.750 + break; 1.751 + case AMOTION_EVENT_ACTION_POINTER_DOWN: 1.752 + case AMOTION_EVENT_ACTION_POINTER_UP: 1.753 + case AMOTION_EVENT_ACTION_MOVE: 1.754 + case AMOTION_EVENT_ACTION_HOVER_MOVE: 1.755 + msg = NS_MOUSE_MOVE; 1.756 + break; 1.757 + case AMOTION_EVENT_ACTION_OUTSIDE: 1.758 + case AMOTION_EVENT_ACTION_CANCEL: 1.759 + case AMOTION_EVENT_ACTION_UP: 1.760 + msg = NS_MOUSE_BUTTON_UP; 1.761 + break; 1.762 + default: 1.763 + msg = NS_EVENT_NULL; 1.764 + break; 1.765 + } 1.766 + if (msg != NS_EVENT_NULL) { 1.767 + sendMouseEvent(msg, data, 1.768 + status != nsEventStatus_eConsumeNoDefault); 1.769 + } 1.770 + break; 1.771 + } 1.772 + case UserInputData::KEY_DATA: { 1.773 + if (!mKeyDownCount) { 1.774 + // No pending events, the filter state can be updated. 1.775 + mKeyEventsFiltered = isExpired(data); 1.776 + } 1.777 + 1.778 + mKeyDownCount += (data.action == AKEY_EVENT_ACTION_DOWN) ? 1 : -1; 1.779 + if (mKeyEventsFiltered) { 1.780 + return; 1.781 + } 1.782 + 1.783 + sp<KeyCharacterMap> kcm = mEventHub->getKeyCharacterMap(data.deviceId); 1.784 + KeyEventDispatcher dispatcher(data, kcm.get()); 1.785 + dispatcher.Dispatch(); 1.786 + break; 1.787 + } 1.788 + } 1.789 +} 1.790 + 1.791 +void 1.792 +GeckoInputDispatcher::notifyConfigurationChanged(const NotifyConfigurationChangedArgs*) 1.793 +{ 1.794 +} 1.795 + 1.796 +void 1.797 +GeckoInputDispatcher::notifyKey(const NotifyKeyArgs* args) 1.798 +{ 1.799 + UserInputData data; 1.800 + data.timeMs = nanosecsToMillisecs(args->eventTime); 1.801 + data.type = UserInputData::KEY_DATA; 1.802 + data.action = args->action; 1.803 + data.flags = args->flags; 1.804 + data.metaState = args->metaState; 1.805 + data.deviceId = args->deviceId; 1.806 + data.key.keyCode = args->keyCode; 1.807 + data.key.scanCode = args->scanCode; 1.808 + { 1.809 + MutexAutoLock lock(mQueueLock); 1.810 + mEventQueue.push(data); 1.811 + } 1.812 + gAppShell->NotifyNativeEvent(); 1.813 +} 1.814 + 1.815 + 1.816 +void 1.817 +GeckoInputDispatcher::notifyMotion(const NotifyMotionArgs* args) 1.818 +{ 1.819 + UserInputData data; 1.820 + data.timeMs = nanosecsToMillisecs(args->eventTime); 1.821 + data.type = UserInputData::MOTION_DATA; 1.822 + data.action = args->action; 1.823 + data.flags = args->flags; 1.824 + data.metaState = args->metaState; 1.825 + data.deviceId = args->deviceId; 1.826 + MOZ_ASSERT(args->pointerCount <= MAX_POINTERS); 1.827 + data.motion.touchCount = args->pointerCount; 1.828 + for (uint32_t i = 0; i < args->pointerCount; ++i) { 1.829 + ::Touch& touch = data.motion.touches[i]; 1.830 + touch.id = args->pointerProperties[i].id; 1.831 + memcpy(&touch.coords, &args->pointerCoords[i], sizeof(*args->pointerCoords)); 1.832 + } 1.833 + { 1.834 + MutexAutoLock lock(mQueueLock); 1.835 + if (!mEventQueue.empty() && 1.836 + mEventQueue.back().type == UserInputData::MOTION_DATA && 1.837 + ((mEventQueue.back().action & AMOTION_EVENT_ACTION_MASK) == 1.838 + AMOTION_EVENT_ACTION_MOVE || 1.839 + (mEventQueue.back().action & AMOTION_EVENT_ACTION_MASK) == 1.840 + AMOTION_EVENT_ACTION_HOVER_MOVE)) 1.841 + mEventQueue.back() = data; 1.842 + else 1.843 + mEventQueue.push(data); 1.844 + } 1.845 + gAppShell->NotifyNativeEvent(); 1.846 +} 1.847 + 1.848 + 1.849 + 1.850 +void GeckoInputDispatcher::notifySwitch(const NotifySwitchArgs* args) 1.851 +{ 1.852 + if (!sDevInputAudioJack) 1.853 + return; 1.854 + 1.855 + bool needSwitchUpdate = false; 1.856 + 1.857 + if (args->switchMask & (1 << SW_HEADPHONE_INSERT)) { 1.858 + sHeadphoneState = (args->switchValues & (1 << SW_HEADPHONE_INSERT)) ? 1.859 + AKEY_STATE_DOWN : AKEY_STATE_UP; 1.860 + needSwitchUpdate = true; 1.861 + } 1.862 + 1.863 + if (args->switchMask & (1 << SW_MICROPHONE_INSERT)) { 1.864 + sMicrophoneState = (args->switchValues & (1 << SW_MICROPHONE_INSERT)) ? 1.865 + AKEY_STATE_DOWN : AKEY_STATE_UP; 1.866 + needSwitchUpdate = true; 1.867 + } 1.868 + 1.869 + if (needSwitchUpdate) 1.870 + updateHeadphoneSwitch(); 1.871 +} 1.872 + 1.873 +void GeckoInputDispatcher::notifyDeviceReset(const NotifyDeviceResetArgs* args) 1.874 +{ 1.875 +} 1.876 + 1.877 +int32_t GeckoInputDispatcher::injectInputEvent( 1.878 + const InputEvent* event, 1.879 + int32_t injectorPid, int32_t injectorUid, int32_t syncMode, 1.880 + int32_t timeoutMillis, uint32_t policyFlags) 1.881 +{ 1.882 + return INPUT_EVENT_INJECTION_SUCCEEDED; 1.883 +} 1.884 + 1.885 +void 1.886 +GeckoInputDispatcher::setInputWindows(const android::Vector<sp<InputWindowHandle> >& inputWindowHandles) 1.887 +{ 1.888 +} 1.889 + 1.890 +void 1.891 +GeckoInputDispatcher::setFocusedApplication(const sp<InputApplicationHandle>& inputApplicationHandle) 1.892 +{ 1.893 +} 1.894 + 1.895 +void 1.896 +GeckoInputDispatcher::setInputDispatchMode(bool enabled, bool frozen) 1.897 +{ 1.898 +} 1.899 + 1.900 +status_t 1.901 +GeckoInputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel, 1.902 + const sp<InputWindowHandle>& inputWindowHandle, bool monitor) 1.903 +{ 1.904 + return OK; 1.905 +} 1.906 + 1.907 +status_t 1.908 +GeckoInputDispatcher::unregisterInputChannel(const sp<InputChannel>& inputChannel) 1.909 +{ 1.910 + return OK; 1.911 +} 1.912 + 1.913 +nsAppShell::nsAppShell() 1.914 + : mNativeCallbackRequest(false) 1.915 + , mEnableDraw(false) 1.916 + , mHandlers() 1.917 +{ 1.918 + gAppShell = this; 1.919 +} 1.920 + 1.921 +nsAppShell::~nsAppShell() 1.922 +{ 1.923 + // mReaderThread and mEventHub will both be null if InitInputDevices 1.924 + // is not called. 1.925 + if (mReaderThread.get()) { 1.926 + // We separate requestExit() and join() here so we can wake the EventHub's 1.927 + // input loop, and stop it from polling for input events 1.928 + mReaderThread->requestExit(); 1.929 + mEventHub->wake(); 1.930 + 1.931 + status_t result = mReaderThread->requestExitAndWait(); 1.932 + if (result) 1.933 + LOG("Could not stop reader thread - %d", result); 1.934 + } 1.935 + gAppShell = nullptr; 1.936 +} 1.937 + 1.938 +nsresult 1.939 +nsAppShell::Init() 1.940 +{ 1.941 + nsresult rv = nsBaseAppShell::Init(); 1.942 + NS_ENSURE_SUCCESS(rv, rv); 1.943 + 1.944 + epollfd = epoll_create(16); 1.945 + NS_ENSURE_TRUE(epollfd >= 0, NS_ERROR_UNEXPECTED); 1.946 + 1.947 + int ret = pipe2(signalfds, O_NONBLOCK); 1.948 + NS_ENSURE_FALSE(ret, NS_ERROR_UNEXPECTED); 1.949 + 1.950 + rv = AddFdHandler(signalfds[0], pipeHandler, ""); 1.951 + NS_ENSURE_SUCCESS(rv, rv); 1.952 + 1.953 + InitGonkMemoryPressureMonitoring(); 1.954 + 1.955 + if (XRE_GetProcessType() == GeckoProcessType_Default) { 1.956 +#ifdef MOZ_OMX_DECODER 1.957 + android::MediaResourceManagerService::instantiate(); 1.958 +#endif 1.959 +#if ANDROID_VERSION >= 18 && (defined(MOZ_OMX_DECODER) || defined(MOZ_B2G_CAMERA)) 1.960 + android::FakeSurfaceComposer::instantiate(); 1.961 +#endif 1.962 + GonkPermissionService::instantiate(); 1.963 + 1.964 + // Causes the kernel timezone to be set, which in turn causes the 1.965 + // timestamps on SD cards to have the local time rather than UTC time. 1.966 + hal::SetTimezone(hal::GetTimezone()); 1.967 + } 1.968 + 1.969 + nsCOMPtr<nsIObserverService> obsServ = GetObserverService(); 1.970 + if (obsServ) { 1.971 + obsServ->AddObserver(this, "browser-ui-startup-complete", false); 1.972 + obsServ->AddObserver(this, "network-connection-state-changed", false); 1.973 + } 1.974 + 1.975 +#ifdef MOZ_NUWA_PROCESS 1.976 + // Make sure main thread was woken up after Nuwa fork. 1.977 + NuwaAddConstructor((void (*)(void *))&NotifyEvent, nullptr); 1.978 +#endif 1.979 + 1.980 + // Delay initializing input devices until the screen has been 1.981 + // initialized (and we know the resolution). 1.982 + return rv; 1.983 +} 1.984 + 1.985 +NS_IMETHODIMP 1.986 +nsAppShell::Observe(nsISupports* aSubject, 1.987 + const char* aTopic, 1.988 + const char16_t* aData) 1.989 +{ 1.990 + if (!strcmp(aTopic, "network-connection-state-changed")) { 1.991 + NS_ConvertUTF16toUTF8 type(aData); 1.992 + if (!type.IsEmpty()) { 1.993 + hal::NotifyNetworkChange(hal::NetworkInformation(atoi(type.get()), 0, 0)); 1.994 + } 1.995 + return NS_OK; 1.996 + } else if (!strcmp(aTopic, "browser-ui-startup-complete")) { 1.997 + if (sDevInputAudioJack) { 1.998 + sHeadphoneState = mReader->getSwitchState(-1, AINPUT_SOURCE_SWITCH, SW_HEADPHONE_INSERT); 1.999 + sMicrophoneState = mReader->getSwitchState(-1, AINPUT_SOURCE_SWITCH, SW_MICROPHONE_INSERT); 1.1000 + updateHeadphoneSwitch(); 1.1001 + } 1.1002 + mEnableDraw = true; 1.1003 + NotifyEvent(); 1.1004 + return NS_OK; 1.1005 + } 1.1006 + 1.1007 + return nsBaseAppShell::Observe(aSubject, aTopic, aData); 1.1008 +} 1.1009 + 1.1010 +NS_IMETHODIMP 1.1011 +nsAppShell::Exit() 1.1012 +{ 1.1013 + OrientationObserver::ShutDown(); 1.1014 + nsCOMPtr<nsIObserverService> obsServ = GetObserverService(); 1.1015 + if (obsServ) { 1.1016 + obsServ->RemoveObserver(this, "browser-ui-startup-complete"); 1.1017 + obsServ->RemoveObserver(this, "network-connection-state-changed"); 1.1018 + } 1.1019 + return nsBaseAppShell::Exit(); 1.1020 +} 1.1021 + 1.1022 +void 1.1023 +nsAppShell::InitInputDevices() 1.1024 +{ 1.1025 + char value[PROPERTY_VALUE_MAX]; 1.1026 + property_get("ro.moz.devinputjack", value, "0"); 1.1027 + sDevInputAudioJack = !strcmp(value, "1"); 1.1028 + sHeadphoneState = AKEY_STATE_UNKNOWN; 1.1029 + sMicrophoneState = AKEY_STATE_UNKNOWN; 1.1030 + 1.1031 + mEventHub = new EventHub(); 1.1032 + mReaderPolicy = new GeckoInputReaderPolicy(); 1.1033 + mReaderPolicy->setDisplayInfo(); 1.1034 + mDispatcher = new GeckoInputDispatcher(mEventHub); 1.1035 + 1.1036 + mReader = new InputReader(mEventHub, mReaderPolicy, mDispatcher); 1.1037 + mReaderThread = new InputReaderThread(mReader); 1.1038 + 1.1039 + status_t result = mReaderThread->run("InputReader", PRIORITY_URGENT_DISPLAY); 1.1040 + if (result) { 1.1041 + LOG("Failed to initialize InputReader thread, bad things are going to happen..."); 1.1042 + } 1.1043 +} 1.1044 + 1.1045 +nsresult 1.1046 +nsAppShell::AddFdHandler(int fd, FdHandlerCallback handlerFunc, 1.1047 + const char* deviceName) 1.1048 +{ 1.1049 + epoll_event event = { 1.1050 + EPOLLIN, 1.1051 + { 0 } 1.1052 + }; 1.1053 + 1.1054 + FdHandler *handler = mHandlers.AppendElement(); 1.1055 + handler->fd = fd; 1.1056 + strncpy(handler->name, deviceName, sizeof(handler->name) - 1); 1.1057 + handler->func = handlerFunc; 1.1058 + event.data.u32 = mHandlers.Length() - 1; 1.1059 + return epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &event) ? 1.1060 + NS_ERROR_UNEXPECTED : NS_OK; 1.1061 +} 1.1062 + 1.1063 +void 1.1064 +nsAppShell::ScheduleNativeEventCallback() 1.1065 +{ 1.1066 + mNativeCallbackRequest = true; 1.1067 + NotifyEvent(); 1.1068 +} 1.1069 + 1.1070 +bool 1.1071 +nsAppShell::ProcessNextNativeEvent(bool mayWait) 1.1072 +{ 1.1073 + PROFILER_LABEL("nsAppShell", "ProcessNextNativeEvent"); 1.1074 + epoll_event events[16] = {{ 0 }}; 1.1075 + 1.1076 + int event_count; 1.1077 + { 1.1078 + PROFILER_LABEL("nsAppShell", "ProcessNextNativeEvent::Wait"); 1.1079 + if ((event_count = epoll_wait(epollfd, events, 16, mayWait ? -1 : 0)) <= 0) 1.1080 + return true; 1.1081 + } 1.1082 + 1.1083 + for (int i = 0; i < event_count; i++) 1.1084 + mHandlers[events[i].data.u32].run(); 1.1085 + 1.1086 + if (mDispatcher.get()) 1.1087 + mDispatcher->dispatchOnce(); 1.1088 + 1.1089 + // NativeEventCallback always schedules more if it needs it 1.1090 + // so we can coalesce these. 1.1091 + // See the implementation in nsBaseAppShell.cpp for more info 1.1092 + if (mNativeCallbackRequest) { 1.1093 + mNativeCallbackRequest = false; 1.1094 + NativeEventCallback(); 1.1095 + } 1.1096 + 1.1097 + if (gDrawRequest && mEnableDraw) { 1.1098 + gDrawRequest = false; 1.1099 + nsWindow::DoDraw(); 1.1100 + } 1.1101 + 1.1102 + return true; 1.1103 +} 1.1104 + 1.1105 +void 1.1106 +nsAppShell::NotifyNativeEvent() 1.1107 +{ 1.1108 + write(signalfds[1], "w", 1); 1.1109 +} 1.1110 + 1.1111 +/* static */ void 1.1112 +nsAppShell::NotifyScreenInitialized() 1.1113 +{ 1.1114 + gAppShell->InitInputDevices(); 1.1115 + 1.1116 + // Getting the instance of OrientationObserver to initialize it. 1.1117 + OrientationObserver::GetInstance(); 1.1118 +} 1.1119 + 1.1120 +/* static */ void 1.1121 +nsAppShell::NotifyScreenRotation() 1.1122 +{ 1.1123 + gAppShell->mReaderPolicy->setDisplayInfo(); 1.1124 + gAppShell->mReader->requestRefreshConfiguration(InputReaderConfiguration::CHANGE_DISPLAY_INFO); 1.1125 + 1.1126 + hal::NotifyScreenConfigurationChange(nsScreenGonk::GetConfiguration()); 1.1127 +}