• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 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 package com.android.emulator.multidisplay;
18 
19 import android.app.Service;
20 import android.content.Intent;
21 import android.os.Handler;
22 import android.os.IBinder;
23 import android.util.DebugUtils;
24 import android.util.Log;
25 import android.hardware.display.DisplayManager;
26 import android.hardware.display.VirtualDisplay;
27 import android.view.Surface;
28 
29 import java.io.FileDescriptor;
30 import java.io.PrintWriter;
31 import java.util.ArrayList;
32 import java.util.Arrays;
33 import java.util.List;
34 import java.util.stream.Collectors;
35 
36 import android.os.Messenger;
37 
38 public final class MultiDisplayService extends Service {
39     private static final String TAG = "MultiDisplayService";
40     private static final String DISPLAY_NAME = "Emulator 2D Display";
41     private static final String[] UNIQUE_DISPLAY_ID = new String[]{"notUsed", "1234562",
42                                                                    "1234563", "1234564",
43                                                                    "1234565", "1234566",
44                                                                    "1234567", "1234568",
45                                                                    "1234569", "1234570",
46                                                                    "1234571"};
47     private static final int MAX_DISPLAYS = 10;
48     private static final int ADD = 1;
49     private static final int DEL = 2;
50 
51     private static final int FLAGS = DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC |
52                                       DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY |
53                                       DisplayManager.VIRTUAL_DISPLAY_FLAG_ROTATES_WITH_CONTENT |
54                                       DisplayManager.VIRTUAL_DISPLAY_FLAG_TRUSTED |
55                                       1 << 6 |//DisplayManager.VIRTUAL_DISPLAY_FLAG_SUPPORTS_TOUCH
56                                       1 << 9; //DisplayManager.VIRTUAL_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS;
57 
58     private DisplayManager mDisplayManager;
59     private VirtualDisplay mVirtualDisplay[];
60     private Surface mSurface[];
61     private Messenger mMessenger;
62     private ListenerThread mListener;
63 
64     private final Handler mHandler = new Handler();
65 
66     final class MultiDisplay {
67         public int width;
68         public int height;
69         public int dpi;
70         public int flag;
71         public VirtualDisplay virtualDisplay;
72         public Surface surface;
73         public boolean enabled;
74 
MultiDisplay()75         MultiDisplay() {
76             clear();
77         }
78 
clear()79         public void clear() {
80             width = 0;
81             height = 0;
82             dpi = 0;
83             flag = 0;
84             virtualDisplay = null;
85             surface = null;
86             enabled = false;
87         }
88 
set(int w, int h, int d, int f)89         public void set(int w, int h, int d, int f) {
90             width = w;
91             height = h;
92             dpi = d;
93             flag = f;
94             enabled = true;
95         }
96 
match(int w, int h, int d, int f)97         public boolean match(int w, int h, int d, int f) {
98             return (w == width && h == height && d == dpi && f == flag);
99         }
100 
dump(PrintWriter writer, String prefix, boolean printHeader)101         private void dump(PrintWriter writer, String prefix, boolean printHeader) {
102             if (!enabled) {
103                 if (printHeader) {
104                     writer.println("disabled");
105                 }
106                 return;
107             }
108             if (printHeader) {
109                 writer.println("enabled");
110             }
111             writer.printf("%sDimensions: %dx%d\n", prefix, width, height);
112             writer.printf("%sDPI: %d\n", prefix, dpi);
113             writer.printf("%sflags: %s\n", prefix, flagsToString(flag));
114             writer.printf("%svirtualDisplay: %s\n", prefix, virtualDisplay);
115             writer.printf("%ssurface: %s\n", prefix, surface);
116         }
117 
118         @Override
toString()119         public String toString() {
120             return "MultiDisplay[dimensions=" + width + "x" + height
121                     + ", dpi=" + dpi
122                     + ", enabled=" + enabled
123                     + ", flags=" + flagsToString(flag)
124                     + ", virtualDisplay=" + virtualDisplay
125                     + ", surface=" + surface
126                     + "]";
127         }
128     }
129     private MultiDisplay mMultiDisplay[];
130 
131     @Override
onCreate()132     public void onCreate() {
133         Log.d(TAG, "Creating service");
134 
135         super.onCreate();
136 
137         try {
138             System.loadLibrary("emulator_multidisplay_jni");
139         } catch (Exception e) {
140             Log.e(TAG, "Failed to loadLibrary: " + e);
141         }
142 
143         mListener = new ListenerThread();
144         mListener.start();
145 
146         mDisplayManager = getSystemService(DisplayManager.class);
147         mMultiDisplay = new MultiDisplay[MAX_DISPLAYS + 1];
148         for (int i = 0; i < MAX_DISPLAYS + 1; i++) {
149             Log.d(TAG, "Creating display " + i);
150             mMultiDisplay[i] = new MultiDisplay();
151         }
152     }
153 
154     @Override
onBind(Intent intent)155     public IBinder onBind(Intent intent) {
156         if(mMessenger == null)
157             mMessenger = new Messenger(mHandler);
158         return mMessenger.getBinder();
159     }
160 
161     @Override
onStartCommand(Intent intent, int flags, int startId)162     public int onStartCommand(Intent intent, int flags, int startId) {
163         // keep it alive.
164         return START_STICKY;
165     }
166 
167     @Override
dump(FileDescriptor fd, PrintWriter writer, String[] args)168     protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
169         if (args == null || args.length == 0) {
170             dump(writer);
171         } else {
172             runCmd(writer, args);
173         }
174     };
175 
dump(PrintWriter writer)176     private void dump(PrintWriter writer) {
177        writer.printf("Max displays: %d\n", MAX_DISPLAYS);
178        writer.printf("Unique display ids: %s\n", Arrays.toString(UNIQUE_DISPLAY_ID));
179        writer.printf("Default flags: %s\n", flagsToString(FLAGS));
180        dumpArray(writer, mVirtualDisplay, "virtual display");
181        dumpArray(writer, mSurface, "surface");
182 
183        if (mMultiDisplay != null) {
184            int size = mMultiDisplay.length;
185            writer.printf("# of multi displays: %d\n", size);
186            for (int i = 0; i < size; i++) {
187                writer.printf("  MultiDisplay #%d: ", i);
188                mMultiDisplay[i].dump(writer, "    ", /* printHeader= */ true);
189            }
190        } else {
191            writer.println("No multi display");
192        }
193     }
194 
dumpArray(PrintWriter writer, Object[] array, String name)195     private void dumpArray(PrintWriter writer, Object[] array, String name) {
196         if (array != null) {
197             int size = array.length;
198             writer.printf("# of %ss: %d\n", name, size);
199             for (int i = 0; i < size; i++) {
200                 writer.printf("  %d: %s\n", i, array[i]);
201             }
202         } else {
203             writer.printf("No %s\n", name);
204         }
205     }
206 
runCmd(PrintWriter writer, String[] args)207     private void runCmd(PrintWriter writer, String[] args) {
208         String cmd = args[0];
209 
210         switch (cmd) {
211             case "add":
212                 runCmdAdd(writer, args);
213                 break;
214             case "del":
215                 runCmdDel(writer, args);
216                 break;
217             case "list":
218                 runCmdList(writer);
219                 break;
220             default:
221                 writer.printf("Invalid command: %s. Valid options are: \n", cmd);
222             case "help":
223                 runCmdHelp(writer);
224         }
225     }
226 
runCmdHelp(PrintWriter writer)227     private void runCmdHelp(PrintWriter writer) {
228         writer.println("  help - shows this help");
229         writer.println("  list - list all virtual displays created by this tool");
230         writer.println("  add <display_id> <width> <height> <dpi> <flags> - add a new virtual "
231                 + "display with the given properties");
232         writer.println("  del <display_id> - delete the given virtual display");
233     }
234 
runCmdList(PrintWriter writer)235     private void runCmdList(PrintWriter writer) {
236         if (mMultiDisplay == null) {
237             writer.println("No multi display");
238             return;
239         }
240 
241         List<MultiDisplay> enabledDisplays = Arrays.stream(mMultiDisplay).filter(d -> d.enabled)
242                 .collect(Collectors.toList());
243 
244         if (enabledDisplays.isEmpty()) {
245             writer.println("No multi display added by the tool");
246             return;
247         }
248 
249         int size = enabledDisplays.size();
250         writer.printf("%d display%s\n", size, (size == 1? "" : "s"));
251         for (int i = 0; i < size; i++) {
252             writer.printf("Display %d:\n", i);
253             enabledDisplays.get(i).dump(writer, "  ", /* printHeader= */ false);
254         }
255     }
256 
runCmdAdd(PrintWriter writer, String[] args)257     private void runCmdAdd(PrintWriter writer, String[] args) {
258         if (!hasExactlyArgs(writer, args, 6)) return;
259 
260         int displayId = getIntArg(writer, args, 1);
261         int width = getIntArg(writer, args, 2);
262         int height = getIntArg(writer, args, 3);
263         int dpi = getIntArg(writer, args, 4);
264         int flags = getIntArg(writer, args, 5);
265 
266         addVirtualDisplay(displayId, width, height, dpi, flags);
267 
268         writer.printf("Display %d added \n", displayId);
269     }
270 
runCmdDel(PrintWriter writer, String[] args)271     private void runCmdDel(PrintWriter writer, String[] args) {
272         if (!hasExactlyArgs(writer, args, 2)) return;
273 
274         int displayId = getIntArg(writer, args, 1);
275 
276         deleteVirtualDisplay(displayId);
277 
278         writer.printf("Display %d deleted\n", displayId);
279     }
280 
hasExactlyArgs(PrintWriter writer, String[] args, int expectedSize)281     private boolean hasExactlyArgs(PrintWriter writer, String[] args, int expectedSize) {
282         if (args.length != expectedSize) {
283             writer.printf("invalid number of arguments (%d) for command %s (expected %d).\n"
284                     + "Valid command:\n",
285                     args.length, args[0], expectedSize);
286             runCmdHelp(writer);
287             return false;
288         }
289         return true;
290     }
291 
getIntArg(PrintWriter writer, String[] args, int index)292     private int getIntArg(PrintWriter writer, String[] args, int index) {
293         String value = "TBD";
294         try {
295             value = args[index];
296             return Integer.parseInt(value);
297         } catch (Exception e) {
298             throw new IllegalArgumentException("invalid integer at index " + index + ": " + value);
299         }
300     }
301 
deleteVirtualDisplay(int displayId)302     private void deleteVirtualDisplay(int displayId) {
303         Log.d(TAG, "deleteVirtualDisplay(" + displayId + ")");
304         if (!mMultiDisplay[displayId].enabled) {
305             return;
306         }
307         if (mMultiDisplay[displayId].virtualDisplay != null) {
308             mMultiDisplay[displayId].virtualDisplay.release();
309         }
310         if (mMultiDisplay[displayId].surface != null) {
311             mMultiDisplay[displayId].surface.release();
312         }
313         mMultiDisplay[displayId].clear();
314         nativeReleaseListener(displayId);
315     }
316 
createVirtualDisplay(int displayId, int w, int h, int dpi, int flag)317     private void createVirtualDisplay(int displayId, int w, int h, int dpi, int flag) {
318         mMultiDisplay[displayId].surface = nativeCreateSurface(displayId, w, h);
319         mMultiDisplay[displayId].virtualDisplay = mDisplayManager.createVirtualDisplay(
320                                           null /* projection */,
321                                           DISPLAY_NAME, w, h, dpi,
322                                           mMultiDisplay[displayId].surface, flag,
323                                           null /* callback */,
324                                           null /* handler */,
325                                           UNIQUE_DISPLAY_ID[displayId]);
326         mMultiDisplay[displayId].set(w, h, dpi, flag);
327     }
328 
addVirtualDisplay(int displayId, int w, int h, int dpi, int flag)329     private void addVirtualDisplay(int displayId, int w, int h, int dpi, int flag) {
330         Log.d(TAG, "addVirtualDisplay(id=" + displayId + ", w=" + w + ", h=" + h
331                 + ", dpi=" + dpi + ", flags=" + flagsToString(flag) + ")");
332         if (mMultiDisplay[displayId].match(w, h, dpi, flag)) {
333             return;
334         }
335         if (mMultiDisplay[displayId].virtualDisplay == null) {
336             createVirtualDisplay(displayId, w, h, dpi, flag);
337             return;
338         }
339         if (mMultiDisplay[displayId].flag != flag) {
340             deleteVirtualDisplay(displayId);
341             createVirtualDisplay(displayId, w, h, dpi, flag);
342             return;
343         }
344         if (mMultiDisplay[displayId].width != w || mMultiDisplay[displayId].height != h) {
345             nativeResizeListener(displayId, w, h);
346         }
347         // only dpi changes
348         mMultiDisplay[displayId].virtualDisplay.resize(w, h, dpi);
349         mMultiDisplay[displayId].set(w, h, dpi, flag);
350     }
351 
352     class ListenerThread extends Thread {
ListenerThread()353         ListenerThread() {
354             super(TAG);
355         }
356 
357         @Override
run()358         public void run() {
359             while(nativeOpen() <= 0) {
360                 Log.e(TAG, "failed to open multiDisplay pipe, retry");
361             }
362             while(true) {
363                 int[] array = {0, 0, 0, 0, 0, 0};
364                 if (!nativeReadPipe(array)) {
365                     continue;
366                 }
367                 Log.v(TAG, "run(): array= " + Arrays.toString(array));
368                 switch (array[0]) {
369                     case ADD: {
370                         for (int j = 0; j < 6; j++) {
371                             Log.d(TAG, "received " + array[j]);
372                         }
373                         int i = array[1];
374                         int width = array[2];
375                         int height = array[3];
376                         int dpi = array[4];
377                         int flag = (array[5] != 0) ? array[5] : FLAGS;
378                         if (i < 1 || i > MAX_DISPLAYS || width <=0 || height <=0 || dpi <=0
379                             || flag < 0) {
380                             Log.e(TAG, "invalid parameters for add/modify display");
381                             break;
382                         }
383                         addVirtualDisplay(i, width, height, dpi, flag);
384                         break;
385                     }
386                     case DEL: {
387                         int i = array[1];
388                         if (i < 1 || i > MAX_DISPLAYS) {
389                             Log.e(TAG, "invalid parameters for delete display");
390                             break;
391                         }
392                         deleteVirtualDisplay(i);
393                         break;
394                     }
395                     // TODO(b/231763427): implement LIST
396                 }
397             }
398         }
399     }
400 
flagsToString(int flags)401     private static String flagsToString(int flags) {
402         return DebugUtils.flagsToString(DisplayManager.class, "VIRTUAL_DISPLAY_FLAG_", flags);
403     }
404 
nativeOpen()405     private native int nativeOpen();
nativeCreateSurface(int displayId, int width, int height)406     private native Surface nativeCreateSurface(int displayId, int width, int height);
nativeReadPipe(int[] arr)407     private native boolean nativeReadPipe(int[] arr);
nativeReleaseListener(int displayId)408     private native boolean nativeReleaseListener(int displayId);
nativeResizeListener(int displayId, int with, int height)409     private native boolean nativeResizeListener(int displayId, int with, int height);
410 }
411