1 /*
2 * Copyright (C) 2019 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 // clang-format off
18 #include "../Macros.h"
19 // clang-format on
20
21 #include "CursorInputMapper.h"
22
23 #include "CursorButtonAccumulator.h"
24 #include "CursorScrollAccumulator.h"
25 #include "PointerControllerInterface.h"
26 #include "TouchCursorInputMapperCommon.h"
27
28 #include "input/PrintTools.h"
29
30 namespace android {
31
32 // The default velocity control parameters that has no effect.
33 static const VelocityControlParameters FLAT_VELOCITY_CONTROL_PARAMS{};
34
35 // --- CursorMotionAccumulator ---
36
CursorMotionAccumulator()37 CursorMotionAccumulator::CursorMotionAccumulator() {
38 clearRelativeAxes();
39 }
40
reset(InputDeviceContext & deviceContext)41 void CursorMotionAccumulator::reset(InputDeviceContext& deviceContext) {
42 clearRelativeAxes();
43 }
44
clearRelativeAxes()45 void CursorMotionAccumulator::clearRelativeAxes() {
46 mRelX = 0;
47 mRelY = 0;
48 }
49
process(const RawEvent * rawEvent)50 void CursorMotionAccumulator::process(const RawEvent* rawEvent) {
51 if (rawEvent->type == EV_REL) {
52 switch (rawEvent->code) {
53 case REL_X:
54 mRelX = rawEvent->value;
55 break;
56 case REL_Y:
57 mRelY = rawEvent->value;
58 break;
59 }
60 }
61 }
62
finishSync()63 void CursorMotionAccumulator::finishSync() {
64 clearRelativeAxes();
65 }
66
67 // --- CursorInputMapper ---
68
CursorInputMapper(InputDeviceContext & deviceContext)69 CursorInputMapper::CursorInputMapper(InputDeviceContext& deviceContext)
70 : InputMapper(deviceContext) {}
71
~CursorInputMapper()72 CursorInputMapper::~CursorInputMapper() {}
73
getSources() const74 uint32_t CursorInputMapper::getSources() const {
75 return mSource;
76 }
77
populateDeviceInfo(InputDeviceInfo * info)78 void CursorInputMapper::populateDeviceInfo(InputDeviceInfo* info) {
79 InputMapper::populateDeviceInfo(info);
80
81 if (mParameters.mode == Parameters::Mode::POINTER) {
82 float minX, minY, maxX, maxY;
83 if (mPointerController->getBounds(&minX, &minY, &maxX, &maxY)) {
84 info->addMotionRange(AMOTION_EVENT_AXIS_X, mSource, minX, maxX, 0.0f, 0.0f, 0.0f);
85 info->addMotionRange(AMOTION_EVENT_AXIS_Y, mSource, minY, maxY, 0.0f, 0.0f, 0.0f);
86 }
87 } else {
88 info->addMotionRange(AMOTION_EVENT_AXIS_X, mSource, -1.0f, 1.0f, 0.0f, mXScale, 0.0f);
89 info->addMotionRange(AMOTION_EVENT_AXIS_Y, mSource, -1.0f, 1.0f, 0.0f, mYScale, 0.0f);
90 info->addMotionRange(AMOTION_EVENT_AXIS_RELATIVE_X, mSource, -1.0f, 1.0f, 0.0f, mXScale,
91 0.0f);
92 info->addMotionRange(AMOTION_EVENT_AXIS_RELATIVE_Y, mSource, -1.0f, 1.0f, 0.0f, mYScale,
93 0.0f);
94 }
95 info->addMotionRange(AMOTION_EVENT_AXIS_PRESSURE, mSource, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f);
96
97 if (mCursorScrollAccumulator.haveRelativeVWheel()) {
98 info->addMotionRange(AMOTION_EVENT_AXIS_VSCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f);
99 }
100 if (mCursorScrollAccumulator.haveRelativeHWheel()) {
101 info->addMotionRange(AMOTION_EVENT_AXIS_HSCROLL, mSource, -1.0f, 1.0f, 0.0f, 0.0f, 0.0f);
102 }
103 }
104
dump(std::string & dump)105 void CursorInputMapper::dump(std::string& dump) {
106 dump += INDENT2 "Cursor Input Mapper:\n";
107 dumpParameters(dump);
108 dump += StringPrintf(INDENT3 "XScale: %0.3f\n", mXScale);
109 dump += StringPrintf(INDENT3 "YScale: %0.3f\n", mYScale);
110 dump += StringPrintf(INDENT3 "XPrecision: %0.3f\n", mXPrecision);
111 dump += StringPrintf(INDENT3 "YPrecision: %0.3f\n", mYPrecision);
112 dump += StringPrintf(INDENT3 "HaveVWheel: %s\n",
113 toString(mCursorScrollAccumulator.haveRelativeVWheel()));
114 dump += StringPrintf(INDENT3 "HaveHWheel: %s\n",
115 toString(mCursorScrollAccumulator.haveRelativeHWheel()));
116 dump += StringPrintf(INDENT3 "VWheelScale: %0.3f\n", mVWheelScale);
117 dump += StringPrintf(INDENT3 "HWheelScale: %0.3f\n", mHWheelScale);
118 dump += StringPrintf(INDENT3 "DisplayId: %s\n", toString(mDisplayId).c_str());
119 dump += StringPrintf(INDENT3 "Orientation: %d\n", mOrientation);
120 dump += StringPrintf(INDENT3 "ButtonState: 0x%08x\n", mButtonState);
121 dump += StringPrintf(INDENT3 "Down: %s\n", toString(isPointerDown(mButtonState)));
122 dump += StringPrintf(INDENT3 "DownTime: %" PRId64 "\n", mDownTime);
123 }
124
configure(nsecs_t when,const InputReaderConfiguration * config,uint32_t changes)125 void CursorInputMapper::configure(nsecs_t when, const InputReaderConfiguration* config,
126 uint32_t changes) {
127 InputMapper::configure(when, config, changes);
128
129 if (!changes) { // first time only
130 mCursorScrollAccumulator.configure(getDeviceContext());
131
132 // Configure basic parameters.
133 configureParameters();
134
135 // Configure device mode.
136 switch (mParameters.mode) {
137 case Parameters::Mode::POINTER_RELATIVE:
138 // Should not happen during first time configuration.
139 ALOGE("Cannot start a device in MODE_POINTER_RELATIVE, starting in MODE_POINTER");
140 mParameters.mode = Parameters::Mode::POINTER;
141 [[fallthrough]];
142 case Parameters::Mode::POINTER:
143 mSource = AINPUT_SOURCE_MOUSE;
144 mXPrecision = 1.0f;
145 mYPrecision = 1.0f;
146 mXScale = 1.0f;
147 mYScale = 1.0f;
148 mPointerController = getContext()->getPointerController(getDeviceId());
149 break;
150 case Parameters::Mode::NAVIGATION:
151 mSource = AINPUT_SOURCE_TRACKBALL;
152 mXPrecision = TRACKBALL_MOVEMENT_THRESHOLD;
153 mYPrecision = TRACKBALL_MOVEMENT_THRESHOLD;
154 mXScale = 1.0f / TRACKBALL_MOVEMENT_THRESHOLD;
155 mYScale = 1.0f / TRACKBALL_MOVEMENT_THRESHOLD;
156 break;
157 }
158
159 mVWheelScale = 1.0f;
160 mHWheelScale = 1.0f;
161 }
162
163 const bool configurePointerCapture = mParameters.mode != Parameters::Mode::NAVIGATION &&
164 ((!changes && config->pointerCaptureRequest.enable) ||
165 (changes & InputReaderConfiguration::CHANGE_POINTER_CAPTURE));
166 if (configurePointerCapture) {
167 if (config->pointerCaptureRequest.enable) {
168 if (mParameters.mode == Parameters::Mode::POINTER) {
169 mParameters.mode = Parameters::Mode::POINTER_RELATIVE;
170 mSource = AINPUT_SOURCE_MOUSE_RELATIVE;
171 // Keep PointerController around in order to preserve the pointer position.
172 mPointerController->fade(PointerControllerInterface::Transition::IMMEDIATE);
173 } else {
174 ALOGE("Cannot request pointer capture, device is not in MODE_POINTER");
175 }
176 } else {
177 if (mParameters.mode == Parameters::Mode::POINTER_RELATIVE) {
178 mParameters.mode = Parameters::Mode::POINTER;
179 mSource = AINPUT_SOURCE_MOUSE;
180 } else {
181 ALOGE("Cannot release pointer capture, device is not in MODE_POINTER_RELATIVE");
182 }
183 }
184 bumpGeneration();
185 if (changes) {
186 NotifyDeviceResetArgs args(getContext()->getNextId(), when, getDeviceId());
187 getListener().notifyDeviceReset(&args);
188 }
189 }
190
191 if (!changes || (changes & InputReaderConfiguration::CHANGE_POINTER_SPEED) ||
192 configurePointerCapture) {
193 if (mParameters.mode == Parameters::Mode::POINTER_RELATIVE) {
194 // Disable any acceleration or scaling for the pointer when Pointer Capture is enabled.
195 mPointerVelocityControl.setParameters(FLAT_VELOCITY_CONTROL_PARAMS);
196 mWheelXVelocityControl.setParameters(FLAT_VELOCITY_CONTROL_PARAMS);
197 mWheelYVelocityControl.setParameters(FLAT_VELOCITY_CONTROL_PARAMS);
198 } else {
199 mPointerVelocityControl.setParameters(config->pointerVelocityControlParameters);
200 mWheelXVelocityControl.setParameters(config->wheelVelocityControlParameters);
201 mWheelYVelocityControl.setParameters(config->wheelVelocityControlParameters);
202 }
203 }
204
205 if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO) ||
206 configurePointerCapture) {
207 const bool isPointer = mParameters.mode == Parameters::Mode::POINTER;
208
209 mDisplayId = ADISPLAY_ID_NONE;
210 if (auto viewport = mDeviceContext.getAssociatedViewport(); viewport) {
211 // This InputDevice is associated with a viewport.
212 // Only generate events for the associated display.
213 const bool mismatchedPointerDisplay =
214 isPointer && (viewport->displayId != mPointerController->getDisplayId());
215 mDisplayId = mismatchedPointerDisplay ? std::nullopt
216 : std::make_optional(viewport->displayId);
217 } else if (isPointer) {
218 // The InputDevice is not associated with a viewport, but it controls the mouse pointer.
219 mDisplayId = mPointerController->getDisplayId();
220 }
221
222 mOrientation = DISPLAY_ORIENTATION_0;
223 const bool isOrientedDevice =
224 (mParameters.orientationAware && mParameters.hasAssociatedDisplay);
225 // InputReader works in the un-rotated display coordinate space, so we don't need to do
226 // anything if the device is already orientation-aware. If the device is not
227 // orientation-aware, then we need to apply the inverse rotation of the display so that
228 // when the display rotation is applied later as a part of the per-window transform, we
229 // get the expected screen coordinates. When pointer capture is enabled, we do not apply any
230 // rotations and report values directly from the input device.
231 if (!isOrientedDevice && mDisplayId &&
232 mParameters.mode != Parameters::Mode::POINTER_RELATIVE) {
233 if (auto viewport = config->getDisplayViewportById(*mDisplayId); viewport) {
234 mOrientation = getInverseRotation(viewport->orientation);
235 }
236 }
237
238 bumpGeneration();
239 }
240 }
241
configureParameters()242 void CursorInputMapper::configureParameters() {
243 mParameters.mode = Parameters::Mode::POINTER;
244 String8 cursorModeString;
245 if (getDeviceContext().getConfiguration().tryGetProperty(String8("cursor.mode"),
246 cursorModeString)) {
247 if (cursorModeString == "navigation") {
248 mParameters.mode = Parameters::Mode::NAVIGATION;
249 } else if (cursorModeString != "pointer" && cursorModeString != "default") {
250 ALOGW("Invalid value for cursor.mode: '%s'", cursorModeString.string());
251 }
252 }
253
254 mParameters.orientationAware = false;
255 getDeviceContext().getConfiguration().tryGetProperty(String8("cursor.orientationAware"),
256 mParameters.orientationAware);
257
258 mParameters.hasAssociatedDisplay = false;
259 if (mParameters.mode == Parameters::Mode::POINTER || mParameters.orientationAware) {
260 mParameters.hasAssociatedDisplay = true;
261 }
262 }
263
dumpParameters(std::string & dump)264 void CursorInputMapper::dumpParameters(std::string& dump) {
265 dump += INDENT3 "Parameters:\n";
266 dump += StringPrintf(INDENT4 "HasAssociatedDisplay: %s\n",
267 toString(mParameters.hasAssociatedDisplay));
268 dump += StringPrintf(INDENT4 "Mode: %s\n", ftl::enum_string(mParameters.mode).c_str());
269 dump += StringPrintf(INDENT4 "OrientationAware: %s\n", toString(mParameters.orientationAware));
270 }
271
reset(nsecs_t when)272 void CursorInputMapper::reset(nsecs_t when) {
273 mButtonState = 0;
274 mDownTime = 0;
275
276 mPointerVelocityControl.reset();
277 mWheelXVelocityControl.reset();
278 mWheelYVelocityControl.reset();
279
280 mCursorButtonAccumulator.reset(getDeviceContext());
281 mCursorMotionAccumulator.reset(getDeviceContext());
282 mCursorScrollAccumulator.reset(getDeviceContext());
283
284 InputMapper::reset(when);
285 }
286
process(const RawEvent * rawEvent)287 void CursorInputMapper::process(const RawEvent* rawEvent) {
288 mCursorButtonAccumulator.process(rawEvent);
289 mCursorMotionAccumulator.process(rawEvent);
290 mCursorScrollAccumulator.process(rawEvent);
291
292 if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
293 sync(rawEvent->when, rawEvent->readTime);
294 }
295 }
296
sync(nsecs_t when,nsecs_t readTime)297 void CursorInputMapper::sync(nsecs_t when, nsecs_t readTime) {
298 if (!mDisplayId) {
299 // Ignore events when there is no target display configured.
300 return;
301 }
302
303 int32_t lastButtonState = mButtonState;
304 int32_t currentButtonState = mCursorButtonAccumulator.getButtonState();
305 mButtonState = currentButtonState;
306
307 bool wasDown = isPointerDown(lastButtonState);
308 bool down = isPointerDown(currentButtonState);
309 bool downChanged;
310 if (!wasDown && down) {
311 mDownTime = when;
312 downChanged = true;
313 } else if (wasDown && !down) {
314 downChanged = true;
315 } else {
316 downChanged = false;
317 }
318 nsecs_t downTime = mDownTime;
319 bool buttonsChanged = currentButtonState != lastButtonState;
320 int32_t buttonsPressed = currentButtonState & ~lastButtonState;
321 int32_t buttonsReleased = lastButtonState & ~currentButtonState;
322
323 float deltaX = mCursorMotionAccumulator.getRelativeX() * mXScale;
324 float deltaY = mCursorMotionAccumulator.getRelativeY() * mYScale;
325 bool moved = deltaX != 0 || deltaY != 0;
326
327 // Rotate delta according to orientation.
328 rotateDelta(mOrientation, &deltaX, &deltaY);
329
330 // Move the pointer.
331 PointerProperties pointerProperties;
332 pointerProperties.clear();
333 pointerProperties.id = 0;
334 pointerProperties.toolType = AMOTION_EVENT_TOOL_TYPE_MOUSE;
335
336 PointerCoords pointerCoords;
337 pointerCoords.clear();
338
339 float vscroll = mCursorScrollAccumulator.getRelativeVWheel();
340 float hscroll = mCursorScrollAccumulator.getRelativeHWheel();
341 bool scrolled = vscroll != 0 || hscroll != 0;
342
343 mWheelYVelocityControl.move(when, nullptr, &vscroll);
344 mWheelXVelocityControl.move(when, &hscroll, nullptr);
345
346 mPointerVelocityControl.move(when, &deltaX, &deltaY);
347
348 float xCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION;
349 float yCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION;
350 if (mSource == AINPUT_SOURCE_MOUSE) {
351 if (moved || scrolled || buttonsChanged) {
352 mPointerController->setPresentation(PointerControllerInterface::Presentation::POINTER);
353
354 if (moved) {
355 mPointerController->move(deltaX, deltaY);
356 }
357
358 if (buttonsChanged) {
359 mPointerController->setButtonState(currentButtonState);
360 }
361
362 mPointerController->unfade(PointerControllerInterface::Transition::IMMEDIATE);
363 }
364
365 mPointerController->getPosition(&xCursorPosition, &yCursorPosition);
366
367 pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, xCursorPosition);
368 pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition);
369 pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, deltaX);
370 pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, deltaY);
371 } else {
372 // Pointer capture and navigation modes
373 pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, deltaX);
374 pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, deltaY);
375 pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, deltaX);
376 pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, deltaY);
377 }
378
379 pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, down ? 1.0f : 0.0f);
380
381 // Moving an external trackball or mouse should wake the device.
382 // We don't do this for internal cursor devices to prevent them from waking up
383 // the device in your pocket.
384 // TODO: Use the input device configuration to control this behavior more finely.
385 uint32_t policyFlags = 0;
386 if ((buttonsPressed || moved || scrolled) && getDeviceContext().isExternal()) {
387 policyFlags |= POLICY_FLAG_WAKE;
388 }
389
390 // Synthesize key down from buttons if needed.
391 synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_DOWN, when, readTime, getDeviceId(),
392 mSource, *mDisplayId, policyFlags, lastButtonState, currentButtonState);
393
394 // Send motion event.
395 if (downChanged || moved || scrolled || buttonsChanged) {
396 int32_t metaState = getContext()->getGlobalMetaState();
397 int32_t buttonState = lastButtonState;
398 int32_t motionEventAction;
399 if (downChanged) {
400 motionEventAction = down ? AMOTION_EVENT_ACTION_DOWN : AMOTION_EVENT_ACTION_UP;
401 } else if (down || (mSource != AINPUT_SOURCE_MOUSE)) {
402 motionEventAction = AMOTION_EVENT_ACTION_MOVE;
403 } else {
404 motionEventAction = AMOTION_EVENT_ACTION_HOVER_MOVE;
405 }
406
407 if (buttonsReleased) {
408 BitSet32 released(buttonsReleased);
409 while (!released.isEmpty()) {
410 int32_t actionButton = BitSet32::valueForBit(released.clearFirstMarkedBit());
411 buttonState &= ~actionButton;
412 NotifyMotionArgs releaseArgs(getContext()->getNextId(), when, readTime,
413 getDeviceId(), mSource, *mDisplayId, policyFlags,
414 AMOTION_EVENT_ACTION_BUTTON_RELEASE, actionButton, 0,
415 metaState, buttonState, MotionClassification::NONE,
416 AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
417 &pointerCoords, mXPrecision, mYPrecision,
418 xCursorPosition, yCursorPosition, downTime,
419 /* videoFrames */ {});
420 getListener().notifyMotion(&releaseArgs);
421 }
422 }
423
424 NotifyMotionArgs args(getContext()->getNextId(), when, readTime, getDeviceId(), mSource,
425 *mDisplayId, policyFlags, motionEventAction, 0, 0, metaState,
426 currentButtonState, MotionClassification::NONE,
427 AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, &pointerCoords,
428 mXPrecision, mYPrecision, xCursorPosition, yCursorPosition, downTime,
429 /* videoFrames */ {});
430 getListener().notifyMotion(&args);
431
432 if (buttonsPressed) {
433 BitSet32 pressed(buttonsPressed);
434 while (!pressed.isEmpty()) {
435 int32_t actionButton = BitSet32::valueForBit(pressed.clearFirstMarkedBit());
436 buttonState |= actionButton;
437 NotifyMotionArgs pressArgs(getContext()->getNextId(), when, readTime, getDeviceId(),
438 mSource, *mDisplayId, policyFlags,
439 AMOTION_EVENT_ACTION_BUTTON_PRESS, actionButton, 0,
440 metaState, buttonState, MotionClassification::NONE,
441 AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
442 &pointerCoords, mXPrecision, mYPrecision,
443 xCursorPosition, yCursorPosition, downTime,
444 /* videoFrames */ {});
445 getListener().notifyMotion(&pressArgs);
446 }
447 }
448
449 ALOG_ASSERT(buttonState == currentButtonState);
450
451 // Send hover move after UP to tell the application that the mouse is hovering now.
452 if (motionEventAction == AMOTION_EVENT_ACTION_UP && (mSource == AINPUT_SOURCE_MOUSE)) {
453 NotifyMotionArgs hoverArgs(getContext()->getNextId(), when, readTime, getDeviceId(),
454 mSource, *mDisplayId, policyFlags,
455 AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState,
456 currentButtonState, MotionClassification::NONE,
457 AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
458 &pointerCoords, mXPrecision, mYPrecision, xCursorPosition,
459 yCursorPosition, downTime, /* videoFrames */ {});
460 getListener().notifyMotion(&hoverArgs);
461 }
462
463 // Send scroll events.
464 if (scrolled) {
465 pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_VSCROLL, vscroll);
466 pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_HSCROLL, hscroll);
467
468 NotifyMotionArgs scrollArgs(getContext()->getNextId(), when, readTime, getDeviceId(),
469 mSource, *mDisplayId, policyFlags,
470 AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState,
471 currentButtonState, MotionClassification::NONE,
472 AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties,
473 &pointerCoords, mXPrecision, mYPrecision, xCursorPosition,
474 yCursorPosition, downTime, /* videoFrames */ {});
475 getListener().notifyMotion(&scrollArgs);
476 }
477 }
478
479 // Synthesize key up from buttons if needed.
480 synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_UP, when, readTime, getDeviceId(), mSource,
481 *mDisplayId, policyFlags, lastButtonState, currentButtonState);
482
483 mCursorMotionAccumulator.finishSync();
484 mCursorScrollAccumulator.finishSync();
485 }
486
getScanCodeState(uint32_t sourceMask,int32_t scanCode)487 int32_t CursorInputMapper::getScanCodeState(uint32_t sourceMask, int32_t scanCode) {
488 if (scanCode >= BTN_MOUSE && scanCode < BTN_JOYSTICK) {
489 return getDeviceContext().getScanCodeState(scanCode);
490 } else {
491 return AKEY_STATE_UNKNOWN;
492 }
493 }
494
getAssociatedDisplayId()495 std::optional<int32_t> CursorInputMapper::getAssociatedDisplayId() {
496 return mDisplayId;
497 }
498
499 } // namespace android
500