1 /*
2 * Copyright (C) 2020 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 #include "MultiDisplay.h"
18
19 #include <stddef.h> // for size_t
20 #include <algorithm> // for max
21 #include <cstdint> // for uint32_t
22 #include <ostream> // for operator<<
23 #include <set> // for set
24 #include <string> // for string, stoi
25 #include <unordered_map> // for unordered_map
26 #include <utility> // for pair, make_pair
27 #include <vector> // for vector
28
29 #include "android/base/LayoutResolver.h" // for resolveLayout
30 #include "android/base/Log.h" // for LogStreamVoi...
31 #include "android/base/files/Stream.h" // for Stream
32 #include "android/base/files/StreamSerializing.h" // for loadCollection
33 #include "android/cmdline-option.h" // for android_cmdL...
34 #include "android/emulation/MultiDisplayPipe.h" // for MultiDisplay...
35 #include "android/emulation/control/adb/AdbInterface.h" // for AdbInterface
36 #include "android/emulator-window.h" // for emulator_win...
37 #include "android/featurecontrol/FeatureControl.h" // for isEnabled
38 #include "android/featurecontrol/Features.h" // for MultiDisplay
39 #include "android/globals.h" // for android_hw
40 #include "android/hw-sensors.h" // for android_fold...
41 #include "android/recording/screen-recorder.h" // for RecorderStates
42 #include "android/skin/file.h" // for SkinLayout
43 #include "android/skin/rect.h" // for SKIN_ROTATION_0
44
45 using android::base::AutoLock;
46
47 namespace android {
48
49 static MultiDisplay* sMultiDisplay = nullptr;
50
MultiDisplay(const QAndroidEmulatorWindowAgent * const windowAgent,const QAndroidRecordScreenAgent * const recordAgent,bool isGuestMode)51 MultiDisplay::MultiDisplay(const QAndroidEmulatorWindowAgent* const windowAgent,
52 const QAndroidRecordScreenAgent* const recordAgent,
53 bool isGuestMode)
54 : mWindowAgent(windowAgent),
55 mRecordAgent(recordAgent),
56 mGuestMode(isGuestMode) { }
57
58 //static
getInstance()59 MultiDisplay* MultiDisplay::getInstance() {
60 return sMultiDisplay;
61 }
62
setMultiDisplay(uint32_t id,int32_t x,int32_t y,uint32_t w,uint32_t h,uint32_t dpi,uint32_t flag,bool add)63 int MultiDisplay::setMultiDisplay(uint32_t id,
64 int32_t x,
65 int32_t y,
66 uint32_t w,
67 uint32_t h,
68 uint32_t dpi,
69 uint32_t flag,
70 bool add) {
71 int ret = 0;
72 SkinRotation rotation = SKIN_ROTATION_0;
73 LOG(VERBOSE) << "setMultiDisplay id " << id << " "
74 << x << " " << y << " " << w << " " << h << " "
75 << dpi << " " << flag << " " << (add? "add":"del");
76 if (!featurecontrol::isEnabled(android::featurecontrol::MultiDisplay)) {
77 return -1;
78 }
79 if (android_foldable_any_folded_area_configured()) {
80 return -1;
81 }
82 // TODO (wdu@) Remove this when multidisplay is supported by embedded
83 // emulator.
84 if (android_cmdLineOptions->qt_hide_window) {
85 return -1;
86 }
87 if (mGuestMode) {
88 return -1;
89 }
90 if (add && !multiDisplayParamValidate(id, w, h, dpi, flag)) {
91 return -1;
92 }
93
94 // fetch rotation from EmulatorWindow
95 // TODO: link to libui source code???
96 EmulatorWindow* window = emulator_window_get();
97 if (window) {
98 SkinLayout* layout = emulator_window_get_layout(window);
99 if (layout) {
100 rotation = layout->orientation;
101 }
102 }
103 if (rotation != SKIN_ROTATION_0) {
104 mWindowAgent->showMessage("Please apply multiple displays without rotation",
105 WINDOW_MESSAGE_ERROR, 1000);
106 return -1;
107 }
108
109 if (add) {
110 ret = createDisplay(&id);
111 if (ret != 0) {
112 return ret;
113 }
114 ret = setDisplayPose(id, x, y, w, h, dpi);
115 if (ret != 0) {
116 return ret;
117 }
118 } else {
119 ret = destroyDisplay(id);
120 if (ret != 0) {
121 return ret;
122 }
123 }
124
125 // Service in guest has already started through QemuMiscPipe when
126 // bootCompleted. But this service may be killed, e.g., Android low
127 // memory. Send broadcast again to guarantee servce running.
128 // P.S. guest Service has check to avoid duplication.
129 auto adbInterface = emulation::AdbInterface::getGlobal();
130 if (!adbInterface) {
131 LOG(ERROR) << "Adb interface unavailable";
132 return -1;
133 }
134 adbInterface->enqueueCommand(
135 {"shell", "am", "broadcast", "-a",
136 "com.android.emulator.multidisplay.START", "-n",
137 "com.android.emulator.multidisplay/"
138 ".MultiDisplayServiceReceiver"});
139
140 MultiDisplayPipe* pipe = MultiDisplayPipe::getInstance();
141 if (pipe) {
142 std::vector<uint8_t> data;
143 pipe->fillData(data, id, w, h, dpi, flag, add);
144 LOG(VERBOSE) << "MultiDisplayPipe send " << (add ? "add":"del") << " id " << id
145 << " width " << w << " height " << h << " dpi " << dpi
146 << " flag " << flag;
147 pipe->send(std::move(data));
148 }
149 return 0;
150 }
151
getMultiDisplay(uint32_t id,int32_t * x,int32_t * y,uint32_t * w,uint32_t * h,uint32_t * dpi,uint32_t * flag,uint32_t * cb,bool * enabled)152 bool MultiDisplay::getMultiDisplay(uint32_t id,
153 int32_t* x,
154 int32_t* y,
155 uint32_t* w,
156 uint32_t* h,
157 uint32_t* dpi,
158 uint32_t* flag,
159 uint32_t* cb,
160 bool* enabled) {
161 AutoLock lock(mLock);
162 if (mMultiDisplay.find(id) == mMultiDisplay.end()) {
163 if (enabled) {
164 *enabled = false;
165 }
166 return false;
167 }
168 if (x) {
169 *x = mMultiDisplay[id].pos_x;
170 }
171 if (y) {
172 *y = mMultiDisplay[id].pos_y;
173 }
174 if (w) {
175 *w = mMultiDisplay[id].width;
176 }
177 if (h) {
178 *h = mMultiDisplay[id].height;
179 }
180 if (dpi) {
181 *dpi = mMultiDisplay[id].dpi;
182 }
183 if (flag) {
184 *flag = mMultiDisplay[id].flag;
185 }
186 if (enabled) {
187 *enabled = mMultiDisplay[id].enabled;
188 }
189 LOG(VERBOSE) << "getMultiDisplay " << id << "x " << mMultiDisplay[id].pos_x
190 << " y " << mMultiDisplay[id].pos_y
191 << " w " << mMultiDisplay[id].width
192 << " h " << mMultiDisplay[id].height
193 << " dpi " << mMultiDisplay[id].dpi
194 << " flag " << mMultiDisplay[id].flag
195 << " enable " << mMultiDisplay[id].enabled;
196 return mMultiDisplay[id].enabled;
197 }
198
getNextMultiDisplay(int32_t start_id,uint32_t * id,int32_t * x,int32_t * y,uint32_t * w,uint32_t * h,uint32_t * dpi,uint32_t * flag,uint32_t * cb)199 bool MultiDisplay::getNextMultiDisplay(int32_t start_id,
200 uint32_t* id,
201 int32_t* x,
202 int32_t* y,
203 uint32_t* w,
204 uint32_t* h,
205 uint32_t* dpi,
206 uint32_t* flag,
207 uint32_t* cb) {
208 uint32_t key;
209 std::map<uint32_t, MultiDisplayInfo>::iterator i;
210
211 AutoLock lock(mLock);
212 if (start_id < 0) {
213 key = 0;
214 } else {
215 key = start_id + 1;
216 }
217 i = mMultiDisplay.lower_bound(key);
218 if (i == mMultiDisplay.end()) {
219 return false;
220 } else {
221 if (id) {
222 *id = i->first;
223 }
224 if (x) {
225 *x = i->second.pos_x;
226 }
227 if (y) {
228 *y = i->second.pos_y;
229 }
230 if (w) {
231 *w = i->second.width;
232 }
233 if (h) {
234 *h = i->second.height;
235 }
236 if (dpi) {
237 *dpi = i->second.dpi;
238 }
239 if (flag) {
240 *flag = i->second.flag;
241 }
242 if (cb) {
243 *cb = i->second.cb;
244 }
245 return true;
246 }
247 }
248
translateCoordination(uint32_t * x,uint32_t * y,uint32_t * displayId)249 bool MultiDisplay::translateCoordination(uint32_t* x, uint32_t* y, uint32_t* displayId) {
250 if (mGuestMode) {
251 *displayId = 0;
252 return true;
253 }
254 AutoLock lock(mLock);
255 uint32_t totalH, pos_x, pos_y, w, h;
256 getCombinedDisplaySizeLocked(nullptr, &totalH);
257 for (const auto iter : mMultiDisplay) {
258 if (iter.first != 0 && iter.second.cb == 0) {
259 continue;
260 }
261 // QT window uses the top left corner as the origin.
262 // So we need to transform the (x, y) coordinates from
263 // bottom left corner to top left corner.
264 pos_x = iter.second.pos_x;
265 pos_y = totalH - iter.second.height - iter.second.pos_y;
266 w = iter.second.width;
267 h = iter.second.height;
268 if ((*x - pos_x) < w && (*y - pos_y) < h) {
269 *x = *x - pos_x;
270 *y = *y - pos_y;
271 *displayId = iter.first;
272 return true;
273 }
274 }
275 return false;
276 }
277
setGpuMode(bool isGuestMode,uint32_t w,uint32_t h)278 void MultiDisplay::setGpuMode(bool isGuestMode, uint32_t w, uint32_t h) {
279 mGuestMode = isGuestMode;
280 if (isGuestMode) {
281 // Guest mode will not start renderer, which in turn will not set the
282 // default display from FrameBuffer. So we set display 0 here.
283 AutoLock lock(mLock);
284 mMultiDisplay.emplace(0, MultiDisplayInfo(0, 0, w, h, 0, 0, true, 0));
285 }
286 }
287
createDisplay(uint32_t * displayId)288 int MultiDisplay::createDisplay(uint32_t* displayId) {
289 if (mGuestMode) {
290 return -1;
291 }
292
293 if (displayId == nullptr) {
294 LOG(ERROR) << "null displayId pointer";
295 return -1;
296 }
297
298 AutoLock lock(mLock);
299
300 if (mMultiDisplay.size() > s_maxNumMultiDisplay) {
301 LOG(ERROR) << "cannot create more displays, exceeding limits "
302 << s_maxNumMultiDisplay;
303 return -1;
304 }
305 if (mMultiDisplay.find(*displayId) != mMultiDisplay.end()) {
306 return 0;
307 }
308
309 // displays created by internal rcCommands
310 if (*displayId == s_invalidIdMultiDisplay) {
311 for (int i = s_displayIdInternalBegin; i < s_maxNumMultiDisplay; i++) {
312 if (mMultiDisplay.find(i) == mMultiDisplay.end()) {
313 *displayId = i;
314 break;
315 }
316 }
317 }
318 if (*displayId == s_invalidIdMultiDisplay) {
319 LOG(ERROR) << "cannot create more internaldisplays, exceeding limits " <<
320 s_maxNumMultiDisplay - s_displayIdInternalBegin;
321 return -1;
322 }
323
324 mMultiDisplay.emplace(*displayId, MultiDisplayInfo());
325 LOG(VERBOSE) << "create display " << *displayId;
326 return 0;
327 }
328
destroyDisplay(uint32_t displayId)329 int MultiDisplay::destroyDisplay(uint32_t displayId) {
330 uint32_t width, height;
331 bool needUIUpdate = false;
332 bool restoreSkin = false;
333
334 if (mGuestMode) {
335 return -1;
336 }
337 {
338 AutoLock lock(mLock);
339 if (mMultiDisplay.find(displayId) == mMultiDisplay.end()) {
340 return 0;
341 }
342 needUIUpdate = ((mMultiDisplay[displayId].cb != 0) ? true : false);
343 mMultiDisplay.erase(displayId);
344 if (needUIUpdate) {
345 recomputeLayoutLocked();
346 getCombinedDisplaySizeLocked(&width, &height);
347 if (getNumberActiveMultiDisplaysLocked() == 1) {
348 // only display 0 remains, restore skin
349 restoreSkin = true;
350 }
351 }
352 }
353
354 if (needUIUpdate) {
355 // stop recording of this display if it is happening.
356 RecorderStates states = mRecordAgent->getRecorderState();
357 if (states.displayId == displayId && states.state == RECORDER_RECORDING) {
358 mRecordAgent->stopRecording();
359 }
360 mWindowAgent->setUIDisplayRegion(0, 0, width, height);
361 if (restoreSkin) {
362 mWindowAgent->restoreSkin();
363 }
364 }
365 LOG(VERBOSE) << "delete display " << displayId;
366 return 0;
367 }
368
setDisplayPose(uint32_t displayId,int32_t x,int32_t y,uint32_t w,uint32_t h,uint32_t dpi)369 int MultiDisplay::setDisplayPose(uint32_t displayId,
370 int32_t x,
371 int32_t y,
372 uint32_t w,
373 uint32_t h,
374 uint32_t dpi) {
375 bool UIUpdate = false;
376 bool checkRecording = false;
377 uint32_t width, height;
378 if (mGuestMode) {
379 return -1;
380 }
381 {
382 AutoLock lock(mLock);
383 if (mMultiDisplay.find(displayId) == mMultiDisplay.end()) {
384 LOG(ERROR) << "cannot find display " << displayId;
385 return -1;
386 }
387 if (mMultiDisplay[displayId].cb != 0 &&
388 (mMultiDisplay[displayId].width != w || mMultiDisplay[displayId].height != h)) {
389 checkRecording = true;
390 }
391 mMultiDisplay[displayId].width = w;
392 mMultiDisplay[displayId].height = h;
393 mMultiDisplay[displayId].dpi = dpi;
394 mMultiDisplay[displayId].pos_x = x;
395 mMultiDisplay[displayId].pos_y = y;
396 if (mMultiDisplay[displayId].cb != 0) {
397 if (x == -1 && y == -1) {
398 recomputeLayoutLocked();
399 }
400 getCombinedDisplaySizeLocked(&width, &height);
401 UIUpdate = true;
402 }
403 }
404 if (checkRecording) {
405 // stop recording of this display if it is happening.
406 RecorderStates states = mRecordAgent->getRecorderState();
407 if (states.displayId == displayId && states.state == RECORDER_RECORDING) {
408 mRecordAgent->stopRecording();
409 }
410 }
411 if (UIUpdate) {
412 mWindowAgent->setUIDisplayRegion(0, 0, width, height);
413 }
414 LOG(VERBOSE) << "setDisplayPose " << displayId << " x " << x
415 << " y " << y << " w " << w << " h " << h
416 << " dpi " << dpi;
417 return 0;
418 }
419
getDisplayPose(uint32_t displayId,int32_t * x,int32_t * y,uint32_t * w,uint32_t * h)420 int MultiDisplay::getDisplayPose(uint32_t displayId,
421 int32_t* x,
422 int32_t* y,
423 uint32_t* w,
424 uint32_t* h) {
425 if (mGuestMode) {
426 return -1;
427 }
428 AutoLock lock(mLock);
429 if (mMultiDisplay.find(displayId) == mMultiDisplay.end()) {
430 LOG(ERROR) << "cannot find display " << displayId;
431 return -1;
432 }
433 *x = mMultiDisplay[displayId].pos_x;
434 *y = mMultiDisplay[displayId].pos_y;
435 *w = mMultiDisplay[displayId].width;
436 *h = mMultiDisplay[displayId].height;
437 return 0;
438 }
439
setDisplayColorBuffer(uint32_t displayId,uint32_t colorBuffer)440 int MultiDisplay::setDisplayColorBuffer(uint32_t displayId, uint32_t colorBuffer) {
441 uint32_t width, height;
442 bool noSkin = false;
443 bool needUpdate = false;
444 if (mGuestMode) {
445 return -1;
446 }
447 {
448 AutoLock lock(mLock);
449 if (mMultiDisplay.find(displayId) == mMultiDisplay.end()) {
450 LOG(ERROR) << "cannot find display" << displayId;
451 return -1;
452 }
453 if (mMultiDisplay[displayId].cb == colorBuffer) {
454 return 0;
455 }
456 if (mMultiDisplay[displayId].cb == 0) {
457 mMultiDisplay[displayId].cb = colorBuffer;
458 // first time cb assigned, update the UI
459 needUpdate = true;
460 recomputeLayoutLocked();
461 getCombinedDisplaySizeLocked(&width, &height);
462 if (getNumberActiveMultiDisplaysLocked() == 2) {
463 //disable skin when first display set, index 0 is the default one.
464 noSkin = true;
465 }
466 }
467 mMultiDisplay[displayId].cb = colorBuffer;
468 }
469 if (noSkin) {
470 mWindowAgent->setNoSkin();
471 }
472 if (needUpdate) {
473 // Explicitly adjust host window size
474 mWindowAgent->setUIDisplayRegion(0, 0, width, height);
475 }
476 LOG(VERBOSE) << "setDisplayColorBuffer " << displayId << " cb " << colorBuffer;
477 return 0;
478 }
479
getDisplayColorBuffer(uint32_t displayId,uint32_t * colorBuffer)480 int MultiDisplay::getDisplayColorBuffer(uint32_t displayId, uint32_t* colorBuffer) {
481 if (mGuestMode) {
482 return -1;
483 }
484 AutoLock lock(mLock);
485 if (mMultiDisplay.find(displayId) == mMultiDisplay.end()) {
486 return -1;
487 }
488 *colorBuffer = mMultiDisplay[displayId].cb;
489 return 0;
490 }
491
getColorBufferDisplay(uint32_t colorBuffer,uint32_t * displayId)492 int MultiDisplay::getColorBufferDisplay(uint32_t colorBuffer, uint32_t* displayId) {
493 if (mGuestMode) {
494 return -1;
495 }
496 AutoLock lock(mLock);
497 for (const auto& iter : mMultiDisplay) {
498 if (iter.second.cb == colorBuffer) {
499 *displayId = iter.first;
500 return 0;
501 }
502 }
503 return -1;
504 }
505
getCombinedDisplaySize(uint32_t * w,uint32_t * h)506 void MultiDisplay::getCombinedDisplaySize(uint32_t* w, uint32_t* h) {
507 AutoLock lock(mLock);
508 getCombinedDisplaySizeLocked(w, h);
509 }
510
getCombinedDisplaySizeLocked(uint32_t * w,uint32_t * h)511 void MultiDisplay::getCombinedDisplaySizeLocked(uint32_t* w, uint32_t* h) {
512 uint32_t total_h = 0;
513 uint32_t total_w = 0;
514 for (const auto& iter : mMultiDisplay) {
515 if (iter.first == 0 || iter.second.cb != 0) {
516 total_h = std::max(total_h, iter.second.height + iter.second.pos_y);
517 total_w = std::max(total_w, iter.second.width + iter.second.pos_x);
518 }
519 }
520 if (h)
521 *h = total_h;
522 if (w)
523 *w = total_w;
524 }
525
getNumberActiveMultiDisplaysLocked()526 int MultiDisplay::getNumberActiveMultiDisplaysLocked() {
527 int count = 0;
528 for (const auto& iter : mMultiDisplay) {
529 if (iter.first == 0 || iter.second.cb != 0) {
530 count++;
531 }
532 }
533 return count;
534 }
535
536 /*
537 * Given that there are at most 11 displays, we can iterate through all possible
538 * ways of showing each display in either the first row or the second row. It is
539 * also possible to have an empty row. The best combination is to satisfy the
540 * following two criteria: 1, The combined rectangle which contains all the
541 * displays should have an aspect ratio that is close to the monitor's aspect
542 * ratio. 2, The width of the first row should be close to the width of the
543 * second row.
544 *
545 * Important detail of implementations: the x and y offsets saved in
546 * mMultiDisplay use the bottom-left corner as origin. This coordinates will
547 * be used by glviewport() in Postworker.cpp. However, the x and y offsets saved
548 * by invoking setUIMultiDisplay() will be using top-left corner as origin. Thus,
549 * input coordinates willl be calculated correctly when mouse events are
550 * captured by QT window.
551 *
552 * TODO: We assume all displays pos_x/pos_y is adjustable here. This may
553 * overwrite the specified pos_x/pos_y in setDisplayPos();
554 */
recomputeLayoutLocked()555 void MultiDisplay::recomputeLayoutLocked() {
556 uint32_t monitorWidth, monitorHeight;
557 double monitorAspectRatio = 1.0;
558 if (!mWindowAgent->getMonitorRect(&monitorWidth, &monitorHeight)) {
559 LOG(WARNING) << "Fail to get monitor width and height, use default ratio 1.0";
560 } else {
561 monitorAspectRatio = (double) monitorHeight / (double) monitorWidth;
562 }
563 std::unordered_map<uint32_t, std::pair<uint32_t, uint32_t>> rectangles;
564 for (const auto& iter : mMultiDisplay) {
565 if (iter.first == 0 || iter.second.cb != 0) {
566 rectangles[iter.first] =
567 std::make_pair(iter.second.width, iter.second.height);
568 }
569 }
570 for (const auto& iter :
571 android::base::resolveLayout(rectangles, monitorAspectRatio)) {
572 mMultiDisplay[iter.first].pos_x = iter.second.first;
573 mMultiDisplay[iter.first].pos_y = iter.second.second;
574 }
575 }
576
multiDisplayParamValidate(uint32_t id,uint32_t w,uint32_t h,uint32_t dpi,uint32_t flag)577 bool MultiDisplay::multiDisplayParamValidate(uint32_t id, uint32_t w, uint32_t h,
578 uint32_t dpi, uint32_t flag) {
579 // According the Android 9 CDD,
580 // * 120 <= dpi <= 640
581 // * 320 * (dpi / 160) <= width
582 // * 320 * (dpi / 160) <= height
583 // * Screen aspect ratio cannot be longer (or wider) than 21:9 (or 9:21).
584 //
585 // Also we don't want a screen too big to limit the performance impact.
586 // * 4K might be a good upper limit
587
588 if (dpi < 120 || dpi > 640) {
589 mWindowAgent->showMessage("dpi should be between 120 and 640",
590 WINDOW_MESSAGE_ERROR, 1000);
591 LOG(ERROR) << "dpi should be between 120 and 640";
592 return false;
593 }
594 if (w < 320 * dpi / 160 || h < 320 * dpi / 160) {
595 mWindowAgent->showMessage("width and height should be >= 320dp",
596 WINDOW_MESSAGE_ERROR, 1000);
597 LOG(ERROR) << "width and height should be >= 320dp";
598 return false;
599 }
600 if (!((w <= 4096 && h <= 2160) || (w <= 2160 && h <= 4096))) {
601 mWindowAgent->showMessage("resolution should not exceed 4k (4096*2160)",
602 WINDOW_MESSAGE_ERROR, 1000);
603 LOG(ERROR) << "resolution should not exceed 4k (4096*2160)";
604 return false;
605 }
606 if (w * 21 < h * 9 || w * 9 > h * 21) {
607 mWindowAgent->showMessage("Aspect ratio cannot be longer (or wider) than 21:9 (or 9:21)",
608 WINDOW_MESSAGE_ERROR, 1000);
609 LOG(ERROR) << "Aspect ratio cannot be longer (or wider) than 21:9 (or 9:21)";
610 return false;
611 }
612 if (id > s_maxNumMultiDisplay) {
613 mWindowAgent->showMessage("Display index cannot be more than 3",
614 WINDOW_MESSAGE_ERROR, 1000);
615 LOG(ERROR) << "Display index cannot be more than 3";
616 return false;
617 }
618 return true;
619 }
620
parseConfig()621 std::map<uint32_t, MultiDisplayInfo> MultiDisplay::parseConfig() {
622 std::map<uint32_t, MultiDisplayInfo> ret;
623 if (!android_cmdLineOptions || !android_cmdLineOptions->multidisplay) {
624 return ret;
625 }
626 std::string s = android_cmdLineOptions->multidisplay;
627 std::vector<uint32_t> params;
628 size_t last = 0, next = 0;
629 while ((next = s.find(",", last)) != std::string::npos) {
630 params.push_back(std::stoi(s.substr(last, next - last)));
631 last = next + 1;
632 }
633 params.push_back(std::stoi(s.substr(last)));
634 if (params.size() < 5 || params.size() % 5 != 0) {
635 LOG(ERROR) << "Not enough parameters for multidisplay command";
636 return ret;
637 }
638 int i = 0;
639 for (i = 0; i < params.size(); i+=5) {
640 if (params[i] == 0 || params[i] > 3) {
641 LOG(ERROR) << "multidisplay index should only be 1, 2, or 3";
642 ret.clear();
643 return ret;
644 }
645 if (multiDisplayParamValidate(params[i],
646 params[i + 1],
647 params[i + 2],
648 params[i + 3],
649 params[i + 4])) {
650 LOG(ERROR) << "Invalid index/width/height/dpi settings for multidisplay command";
651 ret.clear();
652 return ret;
653 }
654 ret.emplace(params[i], MultiDisplayInfo(-1, -1, params[i + 1], params[i + 2],
655 params[i + 3], params[i + 4], true));
656 }
657 return ret;
658 }
659
loadConfig()660 void MultiDisplay::loadConfig() {
661 // Get the multidisplay configs from startup parameters, if yes,
662 // override the configs in config.ini
663 // This stage happens before the MultiDisplayPipe created (bootCompleted)
664 // or restored (snapshot). MultiDisplay configs will not send to guest
665 // immediately.
666 // For cold boot, MultiDisplayPipe queries configs when it is created.
667 // For snapshot, MultiDisplayPipe query will not happen, instead,
668 // onLoad() function later may overwrite the multidisplay states to
669 // in sync with guest states.
670 if (!featurecontrol::isEnabled(android::featurecontrol::MultiDisplay)) {
671 return;
672 }
673 if (android_foldable_any_folded_area_configured()) {
674 return;
675 }
676 if (mGuestMode) {
677 return;
678 }
679
680 std::map<uint32_t, MultiDisplayInfo> info = parseConfig();
681 if (info.size()) {
682 LOG(VERBOSE) << "config multidisplay with command-line";
683 for (const auto& i : info) {
684 setMultiDisplay(i.first,
685 -1,
686 -1,
687 i.second.width,
688 i.second.height,
689 i.second.dpi,
690 i.second.flag,
691 true);
692 mWindowAgent->updateUIMultiDisplayPage(i.first);
693 }
694 } else {
695 LOG(VERBOSE) << "config multidisplay with config.ini "
696 << android_hw->hw_display1_width
697 << "x" << android_hw->hw_display1_height << " " <<
698 android_hw->hw_display2_width << "x" <<
699 android_hw->hw_display2_height << " " <<
700 android_hw->hw_display3_width << "x" <<
701 android_hw->hw_display3_height;
702 if (android_hw->hw_display1_width != 0 &&
703 android_hw->hw_display1_height != 0) {
704 LOG(VERBOSE) << " add display 1";
705 setMultiDisplay(1,
706 android_hw->hw_display1_xOffset,
707 android_hw->hw_display1_yOffset,
708 android_hw->hw_display1_width,
709 android_hw->hw_display1_height,
710 android_hw->hw_display1_density,
711 android_hw->hw_display1_flag,
712 true);
713 mWindowAgent->updateUIMultiDisplayPage(1);
714 }
715 if (android_hw->hw_display2_width != 0 &&
716 android_hw->hw_display2_height != 0) {
717 LOG(VERBOSE) << " add display 2";
718 setMultiDisplay(2,
719 android_hw->hw_display2_xOffset,
720 android_hw->hw_display2_yOffset,
721 android_hw->hw_display2_width,
722 android_hw->hw_display2_height,
723 android_hw->hw_display2_density,
724 android_hw->hw_display2_flag,
725 true);
726 mWindowAgent->updateUIMultiDisplayPage(2);
727 }
728 if (android_hw->hw_display3_width != 0 &&
729 android_hw->hw_display3_height != 0) {
730 LOG(VERBOSE) << " add display 3";
731 setMultiDisplay(3,
732 android_hw->hw_display3_xOffset,
733 android_hw->hw_display3_yOffset,
734 android_hw->hw_display3_width,
735 android_hw->hw_display3_height,
736 android_hw->hw_display3_density,
737 android_hw->hw_display3_flag,
738 true);
739 mWindowAgent->updateUIMultiDisplayPage(3);
740 }
741 }
742 }
743
onSave(base::Stream * stream)744 void MultiDisplay::onSave(base::Stream* stream) {
745 AutoLock lock(mLock);
746 base::saveCollection(
747 stream, mMultiDisplay,
748 [](base::Stream* s,
749 const std::map<uint32_t, MultiDisplayInfo>::value_type& pair) {
750 s->putBe32(pair.first);
751 s->putBe32(pair.second.pos_x);
752 s->putBe32(pair.second.pos_y);
753 s->putBe32(pair.second.width);
754 s->putBe32(pair.second.height);
755 s->putBe32(pair.second.dpi);
756 s->putBe32(pair.second.flag);
757 s->putBe32(pair.second.cb);
758 s->putByte(pair.second.enabled);
759 });
760 }
761
onLoad(base::Stream * stream)762 void MultiDisplay::onLoad(base::Stream* stream) {
763 std::map<uint32_t, MultiDisplayInfo> displaysOnLoad;
764 base::loadCollection(stream, &displaysOnLoad,
765 [this](base::Stream* stream) -> std::map<uint32_t, MultiDisplayInfo>::value_type {
766 const uint32_t idx = stream->getBe32();
767 const int32_t pos_x = stream->getBe32();
768 const int32_t pos_y = stream->getBe32();
769 const uint32_t width = stream->getBe32();
770 const uint32_t height = stream->getBe32();
771 const uint32_t dpi = stream->getBe32();
772 const uint32_t flag = stream->getBe32();
773 const uint32_t cb = stream->getBe32();
774 const bool enabled = stream->getByte();
775 return {idx, {pos_x, pos_y, width, height, dpi, flag, enabled, cb}};
776 });
777 // Restore the multidisplays of the snapshot.
778 std::set<uint32_t> ids;
779 uint32_t combinedDisplayWidth = 0;
780 uint32_t combinedDisplayHeight = 0;
781 bool activeBeforeLoad, activeAfterLoad;
782 {
783 AutoLock lock(mLock);
784 for (const auto& iter : mMultiDisplay) {
785 ids.insert(iter.first);
786 }
787 for (const auto& iter: displaysOnLoad) {
788 ids.insert(iter.first);
789 }
790 activeBeforeLoad = getNumberActiveMultiDisplaysLocked() > 1;
791 mMultiDisplay.clear();
792 mMultiDisplay = displaysOnLoad;
793 activeAfterLoad = getNumberActiveMultiDisplaysLocked() > 1;
794 getCombinedDisplaySizeLocked(&combinedDisplayWidth, &combinedDisplayHeight);
795 }
796 if (activeAfterLoad) {
797 if (!activeBeforeLoad) {
798 mWindowAgent->setNoSkin();
799 }
800 mWindowAgent->setUIDisplayRegion(0, 0, combinedDisplayWidth, combinedDisplayHeight);
801 } else {
802 if (activeBeforeLoad) {
803 mWindowAgent->setUIDisplayRegion(0, 0, combinedDisplayWidth, combinedDisplayHeight);
804 mWindowAgent->restoreSkin();
805 }
806 }
807 for (const auto& iter : ids) {
808 mWindowAgent->updateUIMultiDisplayPage(iter);
809 }
810 }
811
812 } // namespace android
813
android_init_multi_display(const QAndroidEmulatorWindowAgent * const windowAgent,const QAndroidRecordScreenAgent * const recordAgent,bool isGuestMode)814 void android_init_multi_display(const QAndroidEmulatorWindowAgent* const windowAgent,
815 const QAndroidRecordScreenAgent* const recordAgent,
816 bool isGuestMode) {
817 android::sMultiDisplay = new android::MultiDisplay(windowAgent, recordAgent, isGuestMode);
818 }
819
820 extern "C" {
android_load_multi_display_config()821 void android_load_multi_display_config() {
822 if (!android::sMultiDisplay) {
823 LOG(ERROR) << "Multidisplay not initiated yet, cannot config";
824 return;
825 }
826 android::sMultiDisplay->loadConfig();
827 }
828 }
829