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