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