blob: acba4f6513a74485703b843a2c8c9952603e341e [file] [log] [blame]
Prabir Pradhanbaa5c822019-08-30 15:27:05 -07001/*
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
Prabir Pradhan9244aea2020-02-05 20:31:40 -080017#include "../Macros.h"
Prabir Pradhanbaa5c822019-08-30 15:27:05 -070018
19#include "MultiTouchInputMapper.h"
Siarhei Vishniakou31977182022-09-30 08:51:23 -070020#if defined(__ANDROID__)
Prabir Pradhanf4d65b12022-02-10 07:15:38 -080021#include <android/sysprop/InputProperties.sysprop.h>
Siarhei Vishniakou31977182022-09-30 08:51:23 -070022#endif
Prabir Pradhanf4d65b12022-02-10 07:15:38 -080023
Prabir Pradhanbaa5c822019-08-30 15:27:05 -070024namespace android {
25
26// --- Constants ---
27
28// Maximum number of slots supported when using the slot-based Multitouch Protocol B.
29static constexpr size_t MAX_SLOTS = 32;
30
31// --- MultiTouchMotionAccumulator ---
32
33MultiTouchMotionAccumulator::MultiTouchMotionAccumulator()
34 : mCurrentSlot(-1),
Prabir Pradhanbaa5c822019-08-30 15:27:05 -070035 mUsingSlotsProtocol(false),
36 mHaveStylus(false) {}
37
Nathaniel R. Lewis26ec2222020-01-10 16:30:54 -080038void MultiTouchMotionAccumulator::configure(InputDeviceContext& deviceContext, size_t slotCount,
Prabir Pradhanbaa5c822019-08-30 15:27:05 -070039 bool usingSlotsProtocol) {
Prabir Pradhanbaa5c822019-08-30 15:27:05 -070040 mUsingSlotsProtocol = usingSlotsProtocol;
Nathaniel R. Lewis26ec2222020-01-10 16:30:54 -080041 mHaveStylus = deviceContext.hasAbsoluteAxis(ABS_MT_TOOL_TYPE);
Siarhei Vishniakoubaf0b162022-02-16 11:12:36 -080042 mSlots = std::vector<Slot>(slotCount);
Prabir Pradhanbaa5c822019-08-30 15:27:05 -070043
Prabir Pradhanafabcde2022-09-27 19:32:43 +000044 mCurrentSlot = -1;
Prabir Pradhanbaa5c822019-08-30 15:27:05 -070045 if (mUsingSlotsProtocol) {
46 // Query the driver for the current slot index and use it as the initial slot
47 // before we start reading events from the device. It is possible that the
48 // current slot index will not be the same as it was when the first event was
49 // written into the evdev buffer, which means the input mapper could start
50 // out of sync with the initial state of the events in the evdev buffer.
51 // In the extremely unlikely case that this happens, the data from
52 // two slots will be confused until the next ABS_MT_SLOT event is received.
53 // This can cause the touch point to "jump", but at least there will be
54 // no stuck touches.
55 int32_t initialSlot;
Prabir Pradhanafabcde2022-09-27 19:32:43 +000056 if (const auto status = deviceContext.getAbsoluteAxisValue(ABS_MT_SLOT, &initialSlot);
57 status == OK) {
58 mCurrentSlot = initialSlot;
59 } else {
60 ALOGD("Could not retrieve current multi-touch slot index. status=%d", status);
Prabir Pradhanbaa5c822019-08-30 15:27:05 -070061 }
Prabir Pradhanbaa5c822019-08-30 15:27:05 -070062 }
63}
64
Prabir Pradhanafabcde2022-09-27 19:32:43 +000065void MultiTouchMotionAccumulator::resetSlots() {
Siarhei Vishniakoubaf0b162022-02-16 11:12:36 -080066 for (Slot& slot : mSlots) {
67 slot.clear();
Prabir Pradhanbaa5c822019-08-30 15:27:05 -070068 }
Prabir Pradhanafabcde2022-09-27 19:32:43 +000069 mCurrentSlot = -1;
Prabir Pradhanbaa5c822019-08-30 15:27:05 -070070}
71
72void MultiTouchMotionAccumulator::process(const RawEvent* rawEvent) {
73 if (rawEvent->type == EV_ABS) {
74 bool newSlot = false;
75 if (mUsingSlotsProtocol) {
76 if (rawEvent->code == ABS_MT_SLOT) {
77 mCurrentSlot = rawEvent->value;
78 newSlot = true;
79 }
80 } else if (mCurrentSlot < 0) {
81 mCurrentSlot = 0;
82 }
83
Siarhei Vishniakoubaf0b162022-02-16 11:12:36 -080084 if (mCurrentSlot < 0 || size_t(mCurrentSlot) >= mSlots.size()) {
Siarhei Vishniakoue3ce49d2020-09-29 19:08:13 -050085 if (DEBUG_POINTERS) {
86 if (newSlot) {
87 ALOGW("MultiTouch device emitted invalid slot index %d but it "
88 "should be between 0 and %zd; ignoring this slot.",
Siarhei Vishniakoubaf0b162022-02-16 11:12:36 -080089 mCurrentSlot, mSlots.size() - 1);
Siarhei Vishniakoue3ce49d2020-09-29 19:08:13 -050090 }
Prabir Pradhanbaa5c822019-08-30 15:27:05 -070091 }
Prabir Pradhanbaa5c822019-08-30 15:27:05 -070092 } else {
Siarhei Vishniakoubaf0b162022-02-16 11:12:36 -080093 Slot& slot = mSlots[mCurrentSlot];
Arthur Hung9ad18942021-06-19 02:04:46 +000094 // If mUsingSlotsProtocol is true, it means the raw pointer has axis info of
95 // ABS_MT_TRACKING_ID and ABS_MT_SLOT, so driver should send a valid trackingId while
96 // updating the slot.
97 if (!mUsingSlotsProtocol) {
Siarhei Vishniakoubaf0b162022-02-16 11:12:36 -080098 slot.mInUse = true;
Arthur Hung9ad18942021-06-19 02:04:46 +000099 }
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700100
101 switch (rawEvent->code) {
102 case ABS_MT_POSITION_X:
Siarhei Vishniakoubaf0b162022-02-16 11:12:36 -0800103 slot.mAbsMTPositionX = rawEvent->value;
104 warnIfNotInUse(*rawEvent, slot);
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700105 break;
106 case ABS_MT_POSITION_Y:
Siarhei Vishniakoubaf0b162022-02-16 11:12:36 -0800107 slot.mAbsMTPositionY = rawEvent->value;
108 warnIfNotInUse(*rawEvent, slot);
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700109 break;
110 case ABS_MT_TOUCH_MAJOR:
Siarhei Vishniakoubaf0b162022-02-16 11:12:36 -0800111 slot.mAbsMTTouchMajor = rawEvent->value;
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700112 break;
113 case ABS_MT_TOUCH_MINOR:
Siarhei Vishniakoubaf0b162022-02-16 11:12:36 -0800114 slot.mAbsMTTouchMinor = rawEvent->value;
115 slot.mHaveAbsMTTouchMinor = true;
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700116 break;
117 case ABS_MT_WIDTH_MAJOR:
Siarhei Vishniakoubaf0b162022-02-16 11:12:36 -0800118 slot.mAbsMTWidthMajor = rawEvent->value;
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700119 break;
120 case ABS_MT_WIDTH_MINOR:
Siarhei Vishniakoubaf0b162022-02-16 11:12:36 -0800121 slot.mAbsMTWidthMinor = rawEvent->value;
122 slot.mHaveAbsMTWidthMinor = true;
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700123 break;
124 case ABS_MT_ORIENTATION:
Siarhei Vishniakoubaf0b162022-02-16 11:12:36 -0800125 slot.mAbsMTOrientation = rawEvent->value;
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700126 break;
127 case ABS_MT_TRACKING_ID:
128 if (mUsingSlotsProtocol && rawEvent->value < 0) {
129 // The slot is no longer in use but it retains its previous contents,
130 // which may be reused for subsequent touches.
Siarhei Vishniakoubaf0b162022-02-16 11:12:36 -0800131 slot.mInUse = false;
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700132 } else {
Siarhei Vishniakoubaf0b162022-02-16 11:12:36 -0800133 slot.mInUse = true;
134 slot.mAbsMTTrackingId = rawEvent->value;
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700135 }
136 break;
137 case ABS_MT_PRESSURE:
Siarhei Vishniakoubaf0b162022-02-16 11:12:36 -0800138 slot.mAbsMTPressure = rawEvent->value;
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700139 break;
140 case ABS_MT_DISTANCE:
Siarhei Vishniakoubaf0b162022-02-16 11:12:36 -0800141 slot.mAbsMTDistance = rawEvent->value;
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700142 break;
143 case ABS_MT_TOOL_TYPE:
Siarhei Vishniakoubaf0b162022-02-16 11:12:36 -0800144 slot.mAbsMTToolType = rawEvent->value;
145 slot.mHaveAbsMTToolType = true;
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700146 break;
147 }
148 }
149 } else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_MT_REPORT) {
150 // MultiTouch Sync: The driver has returned all data for *one* of the pointers.
151 mCurrentSlot += 1;
152 }
153}
154
155void MultiTouchMotionAccumulator::finishSync() {
156 if (!mUsingSlotsProtocol) {
Prabir Pradhanafabcde2022-09-27 19:32:43 +0000157 resetSlots();
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700158 }
159}
160
161bool MultiTouchMotionAccumulator::hasStylus() const {
162 return mHaveStylus;
163}
164
Arthur Hung9ad18942021-06-19 02:04:46 +0000165void MultiTouchMotionAccumulator::warnIfNotInUse(const RawEvent& event, const Slot& slot) {
166 if (!slot.mInUse) {
167 ALOGW("Received unexpected event (0x%0x, 0x%0x) for slot %i with tracking id %i",
168 event.code, event.value, mCurrentSlot, slot.mAbsMTTrackingId);
169 }
170}
171
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700172// --- MultiTouchMotionAccumulator::Slot ---
173
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700174int32_t MultiTouchMotionAccumulator::Slot::getToolType() const {
175 if (mHaveAbsMTToolType) {
176 switch (mAbsMTToolType) {
177 case MT_TOOL_FINGER:
178 return AMOTION_EVENT_TOOL_TYPE_FINGER;
179 case MT_TOOL_PEN:
180 return AMOTION_EVENT_TOOL_TYPE_STYLUS;
Arthur Hung421eb1c2020-01-16 00:09:42 +0800181 case MT_TOOL_PALM:
182 return AMOTION_EVENT_TOOL_TYPE_PALM;
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700183 }
184 }
185 return AMOTION_EVENT_TOOL_TYPE_UNKNOWN;
186}
187
188// --- MultiTouchInputMapper ---
189
Nathaniel R. Lewis26ec2222020-01-10 16:30:54 -0800190MultiTouchInputMapper::MultiTouchInputMapper(InputDeviceContext& deviceContext)
191 : TouchInputMapper(deviceContext) {}
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700192
193MultiTouchInputMapper::~MultiTouchInputMapper() {}
194
Siarhei Vishniakou2935db72022-09-22 13:35:22 -0700195std::list<NotifyArgs> MultiTouchInputMapper::reset(nsecs_t when) {
Prabir Pradhanafabcde2022-09-27 19:32:43 +0000196 // The evdev multi-touch protocol does not allow userspace applications to query the initial or
197 // current state of the pointers at any time. This means if we clear our accumulated state when
198 // resetting the input mapper, there's no way to rebuild the full initial state of the pointers.
199 // We can only wait for updates to all the pointers and axes. Rather than clearing the state and
200 // rebuilding the state from scratch, we work around this kernel API limitation by never
201 // fully clearing any state specific to the multi-touch protocol.
Siarhei Vishniakou2935db72022-09-22 13:35:22 -0700202 return TouchInputMapper::reset(when);
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700203}
204
Siarhei Vishniakou2935db72022-09-22 13:35:22 -0700205std::list<NotifyArgs> MultiTouchInputMapper::process(const RawEvent* rawEvent) {
206 std::list<NotifyArgs> out = TouchInputMapper::process(rawEvent);
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700207
208 mMultiTouchMotionAccumulator.process(rawEvent);
Siarhei Vishniakou2935db72022-09-22 13:35:22 -0700209 return out;
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700210}
211
arthurhungcc7f9802020-04-30 17:55:40 +0800212std::optional<int32_t> MultiTouchInputMapper::getActiveBitId(
213 const MultiTouchMotionAccumulator::Slot& inSlot) {
214 if (mHavePointerIds) {
215 int32_t trackingId = inSlot.getTrackingId();
216 for (BitSet32 idBits(mPointerIdBits); !idBits.isEmpty();) {
217 int32_t n = idBits.clearFirstMarkedBit();
218 if (mPointerTrackingIdMap[n] == trackingId) {
219 return std::make_optional(n);
220 }
221 }
222 }
223 return std::nullopt;
224}
225
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700226void MultiTouchInputMapper::syncTouch(nsecs_t when, RawState* outState) {
227 size_t inCount = mMultiTouchMotionAccumulator.getSlotCount();
228 size_t outCount = 0;
229 BitSet32 newPointerIdBits;
230 mHavePointerIds = true;
231
232 for (size_t inIndex = 0; inIndex < inCount; inIndex++) {
Siarhei Vishniakoubaf0b162022-02-16 11:12:36 -0800233 const MultiTouchMotionAccumulator::Slot& inSlot =
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700234 mMultiTouchMotionAccumulator.getSlot(inIndex);
Siarhei Vishniakoubaf0b162022-02-16 11:12:36 -0800235 if (!inSlot.isInUse()) {
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700236 continue;
237 }
238
Siarhei Vishniakoubaf0b162022-02-16 11:12:36 -0800239 if (inSlot.getToolType() == AMOTION_EVENT_TOOL_TYPE_PALM) {
240 std::optional<int32_t> id = getActiveBitId(inSlot);
arthurhungcc7f9802020-04-30 17:55:40 +0800241 if (id) {
242 outState->rawPointerData.canceledIdBits.markBit(id.value());
Arthur Hung421eb1c2020-01-16 00:09:42 +0800243 }
Siarhei Vishniakoue3ce49d2020-09-29 19:08:13 -0500244 if (DEBUG_POINTERS) {
245 ALOGI("Stop processing slot %zu for it received a palm event from device %s",
246 inIndex, getDeviceName().c_str());
247 }
arthurhungbf89a482020-04-17 17:37:55 +0800248 continue;
Arthur Hung421eb1c2020-01-16 00:09:42 +0800249 }
250
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700251 if (outCount >= MAX_POINTERS) {
Siarhei Vishniakoue3ce49d2020-09-29 19:08:13 -0500252 if (DEBUG_POINTERS) {
Siarhei Vishniakou01747382022-01-20 13:23:27 -0800253 ALOGD("MultiTouch device %s emitted more than maximum of %zu pointers; "
Siarhei Vishniakoue3ce49d2020-09-29 19:08:13 -0500254 "ignoring the rest.",
255 getDeviceName().c_str(), MAX_POINTERS);
256 }
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700257 break; // too many fingers!
258 }
259
260 RawPointerData::Pointer& outPointer = outState->rawPointerData.pointers[outCount];
Siarhei Vishniakoubaf0b162022-02-16 11:12:36 -0800261 outPointer.x = inSlot.getX();
262 outPointer.y = inSlot.getY();
263 outPointer.pressure = inSlot.getPressure();
264 outPointer.touchMajor = inSlot.getTouchMajor();
265 outPointer.touchMinor = inSlot.getTouchMinor();
266 outPointer.toolMajor = inSlot.getToolMajor();
267 outPointer.toolMinor = inSlot.getToolMinor();
268 outPointer.orientation = inSlot.getOrientation();
269 outPointer.distance = inSlot.getDistance();
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700270 outPointer.tiltX = 0;
271 outPointer.tiltY = 0;
272
Siarhei Vishniakoubaf0b162022-02-16 11:12:36 -0800273 outPointer.toolType = inSlot.getToolType();
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700274 if (outPointer.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) {
275 outPointer.toolType = mTouchButtonAccumulator.getToolType();
276 if (outPointer.toolType == AMOTION_EVENT_TOOL_TYPE_UNKNOWN) {
277 outPointer.toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
278 }
279 }
Prabir Pradhanf4d65b12022-02-10 07:15:38 -0800280 if (shouldSimulateStylusWithTouch() &&
281 outPointer.toolType == AMOTION_EVENT_TOOL_TYPE_FINGER) {
282 outPointer.toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS;
283 }
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700284
285 bool isHovering = mTouchButtonAccumulator.getToolType() != AMOTION_EVENT_TOOL_TYPE_MOUSE &&
286 (mTouchButtonAccumulator.isHovering() ||
Siarhei Vishniakoubaf0b162022-02-16 11:12:36 -0800287 (mRawPointerAxes.pressure.valid && inSlot.getPressure() <= 0));
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700288 outPointer.isHovering = isHovering;
289
290 // Assign pointer id using tracking id if available.
291 if (mHavePointerIds) {
Siarhei Vishniakoubaf0b162022-02-16 11:12:36 -0800292 int32_t trackingId = inSlot.getTrackingId();
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700293 int32_t id = -1;
294 if (trackingId >= 0) {
295 for (BitSet32 idBits(mPointerIdBits); !idBits.isEmpty();) {
296 uint32_t n = idBits.clearFirstMarkedBit();
297 if (mPointerTrackingIdMap[n] == trackingId) {
298 id = n;
299 }
300 }
301
302 if (id < 0 && !mPointerIdBits.isFull()) {
303 id = mPointerIdBits.markFirstUnmarkedBit();
304 mPointerTrackingIdMap[id] = trackingId;
305 }
306 }
307 if (id < 0) {
308 mHavePointerIds = false;
309 outState->rawPointerData.clearIdBits();
310 newPointerIdBits.clear();
311 } else {
312 outPointer.id = id;
313 outState->rawPointerData.idToIndex[id] = outCount;
314 outState->rawPointerData.markIdBit(id, isHovering);
315 newPointerIdBits.markBit(id);
316 }
317 }
318 outCount += 1;
319 }
320
321 outState->rawPointerData.pointerCount = outCount;
322 mPointerIdBits = newPointerIdBits;
323
324 mMultiTouchMotionAccumulator.finishSync();
325}
326
327void MultiTouchInputMapper::configureRawPointerAxes() {
328 TouchInputMapper::configureRawPointerAxes();
329
330 getAbsoluteAxisInfo(ABS_MT_POSITION_X, &mRawPointerAxes.x);
331 getAbsoluteAxisInfo(ABS_MT_POSITION_Y, &mRawPointerAxes.y);
332 getAbsoluteAxisInfo(ABS_MT_TOUCH_MAJOR, &mRawPointerAxes.touchMajor);
333 getAbsoluteAxisInfo(ABS_MT_TOUCH_MINOR, &mRawPointerAxes.touchMinor);
334 getAbsoluteAxisInfo(ABS_MT_WIDTH_MAJOR, &mRawPointerAxes.toolMajor);
335 getAbsoluteAxisInfo(ABS_MT_WIDTH_MINOR, &mRawPointerAxes.toolMinor);
336 getAbsoluteAxisInfo(ABS_MT_ORIENTATION, &mRawPointerAxes.orientation);
337 getAbsoluteAxisInfo(ABS_MT_PRESSURE, &mRawPointerAxes.pressure);
338 getAbsoluteAxisInfo(ABS_MT_DISTANCE, &mRawPointerAxes.distance);
339 getAbsoluteAxisInfo(ABS_MT_TRACKING_ID, &mRawPointerAxes.trackingId);
340 getAbsoluteAxisInfo(ABS_MT_SLOT, &mRawPointerAxes.slot);
341
342 if (mRawPointerAxes.trackingId.valid && mRawPointerAxes.slot.valid &&
343 mRawPointerAxes.slot.minValue == 0 && mRawPointerAxes.slot.maxValue > 0) {
344 size_t slotCount = mRawPointerAxes.slot.maxValue + 1;
345 if (slotCount > MAX_SLOTS) {
346 ALOGW("MultiTouch Device %s reported %zu slots but the framework "
347 "only supports a maximum of %zu slots at this time.",
348 getDeviceName().c_str(), slotCount, MAX_SLOTS);
349 slotCount = MAX_SLOTS;
350 }
Nathaniel R. Lewis26ec2222020-01-10 16:30:54 -0800351 mMultiTouchMotionAccumulator.configure(getDeviceContext(), slotCount,
352 true /*usingSlotsProtocol*/);
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700353 } else {
Nathaniel R. Lewis26ec2222020-01-10 16:30:54 -0800354 mMultiTouchMotionAccumulator.configure(getDeviceContext(), MAX_POINTERS,
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700355 false /*usingSlotsProtocol*/);
356 }
357}
358
359bool MultiTouchInputMapper::hasStylus() const {
Prabir Pradhanf4d65b12022-02-10 07:15:38 -0800360 return mMultiTouchMotionAccumulator.hasStylus() || mTouchButtonAccumulator.hasStylus() ||
361 shouldSimulateStylusWithTouch();
362}
363
364bool MultiTouchInputMapper::shouldSimulateStylusWithTouch() const {
365 static const bool SIMULATE_STYLUS_WITH_TOUCH =
Siarhei Vishniakou31977182022-09-30 08:51:23 -0700366#if defined(__ANDROID__)
Prabir Pradhanf4d65b12022-02-10 07:15:38 -0800367 sysprop::InputProperties::simulate_stylus_with_touch().value_or(false);
Siarhei Vishniakou31977182022-09-30 08:51:23 -0700368#else
369 // Disable this developer feature where sysproperties are not available
370 false;
371#endif
Prabir Pradhanf4d65b12022-02-10 07:15:38 -0800372 return SIMULATE_STYLUS_WITH_TOUCH &&
373 mParameters.deviceType == Parameters::DeviceType::TOUCH_SCREEN;
Prabir Pradhanbaa5c822019-08-30 15:27:05 -0700374}
375
376} // namespace android