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.content.Context; 20 import android.app.Service; 21 import android.content.Intent; 22 import android.os.Handler; 23 import android.os.IBinder; 24 import android.util.Log; 25 import android.hardware.display.DisplayManager; 26 import android.hardware.display.VirtualDisplay; 27 import android.view.Surface; 28 import android.os.Messenger; 29 30 31 public class MultiDisplayService extends Service { 32 private static final String TAG = "MultiDisplayService"; 33 private static final String DISPLAY_NAME = "Emulator 2D Display"; 34 private static final String[] UNIQUE_DISPLAY_ID = new String[]{"notUsed", "1234562", 35 "1234563", "1234564", 36 "1234565", "1234566", 37 "1234567", "1234568", 38 "1234569", "1234570", 39 "1234571"}; 40 private static final int MAX_DISPLAYS = 10; 41 private static final int ADD = 1; 42 private static final int DEL = 2; 43 private static final int mFlags = DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC | 44 DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY | 45 DisplayManager.VIRTUAL_DISPLAY_FLAG_ROTATES_WITH_CONTENT | 46 1 << 6 |//DisplayManager.VIRTUAL_DISPLAY_FLAG_SUPPORTS_TOUCH 47 1 << 9; //DisplayManager.VIRTUAL_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS; 48 private DisplayManager mDisplayManager; 49 private VirtualDisplay mVirtualDisplay[]; 50 private Surface mSurface[]; 51 private Messenger mMessenger; 52 private ListenerThread mListener; 53 54 private final Handler mHandler = new Handler(); 55 56 class MultiDisplay { 57 public int width; 58 public int height; 59 public int dpi; 60 public int flag; 61 public VirtualDisplay virtualDisplay; 62 public Surface surface; 63 public boolean enabled; MultiDisplay()64 MultiDisplay() { 65 clear(); 66 } clear()67 public void clear() { 68 width = 0; 69 height = 0; 70 dpi = 0; 71 flag = 0; 72 virtualDisplay = null; 73 surface = null; 74 enabled = false; 75 } set(int w, int h, int d, int f)76 public void set(int w, int h, int d, int f) { 77 width = w; 78 height = h; 79 dpi = d; 80 flag = f; 81 enabled = true; 82 } match(int w, int h, int d, int f)83 public boolean match(int w, int h, int d, int f) { 84 return (w == width && h == height && d == dpi && f == flag); 85 } 86 } 87 private MultiDisplay mMultiDisplay[]; 88 89 @Override onCreate()90 public void onCreate() { 91 super.onCreate(); 92 93 try { 94 System.loadLibrary("emulator_multidisplay_jni"); 95 } catch (Exception e) { 96 Log.e(TAG, "Failed to loadLibrary: " + e); 97 } 98 99 mListener = new ListenerThread(); 100 mListener.start(); 101 102 mDisplayManager = (DisplayManager)getSystemService(Context.DISPLAY_SERVICE); 103 mMultiDisplay = new MultiDisplay[MAX_DISPLAYS + 1]; 104 for (int i = 0; i < MAX_DISPLAYS + 1; i++) { 105 mMultiDisplay[i] = new MultiDisplay(); 106 } 107 } 108 109 @Override onBind(Intent intent)110 public IBinder onBind(Intent intent) { 111 if(mMessenger == null) 112 mMessenger = new Messenger(mHandler); 113 return mMessenger.getBinder(); 114 } 115 116 @Override onStartCommand(Intent intent, int flags, int startId)117 public int onStartCommand(Intent intent, int flags, int startId) { 118 // keep it alive. 119 return START_STICKY; 120 } 121 122 class ListenerThread extends Thread { ListenerThread()123 ListenerThread() { 124 super(TAG); 125 } 126 deleteVirtualDisplay(int displayId)127 private void deleteVirtualDisplay(int displayId) { 128 if (!mMultiDisplay[displayId].enabled) { 129 return; 130 } 131 if (mMultiDisplay[displayId].virtualDisplay != null) { 132 mMultiDisplay[displayId].virtualDisplay.release(); 133 } 134 if (mMultiDisplay[displayId].surface != null) { 135 mMultiDisplay[displayId].surface.release(); 136 } 137 mMultiDisplay[displayId].clear(); 138 nativeReleaseListener(displayId); 139 } 140 createVirtualDisplay(int displayId, int w, int h, int dpi, int flag)141 private void createVirtualDisplay(int displayId, int w, int h, int dpi, int flag) { 142 mMultiDisplay[displayId].surface = nativeCreateSurface(displayId, w, h); 143 mMultiDisplay[displayId].virtualDisplay = mDisplayManager.createVirtualDisplay( 144 null /* projection */, 145 DISPLAY_NAME, w, h, dpi, 146 mMultiDisplay[displayId].surface, flag, 147 null /* callback */, 148 null /* handler */, 149 UNIQUE_DISPLAY_ID[displayId]); 150 mMultiDisplay[displayId].set(w, h, dpi, flag); 151 } 152 addVirtualDisplay(int displayId, int w, int h, int dpi, int flag)153 private void addVirtualDisplay(int displayId, int w, int h, int dpi, int flag) { 154 if (mMultiDisplay[displayId].match(w, h, dpi, flag)) { 155 return; 156 } 157 if (mMultiDisplay[displayId].virtualDisplay == null) { 158 createVirtualDisplay(displayId, w, h, dpi, flag); 159 return; 160 } 161 if (mMultiDisplay[displayId].flag != flag) { 162 deleteVirtualDisplay(displayId); 163 createVirtualDisplay(displayId, w, h, dpi, flag); 164 return; 165 } 166 if (mMultiDisplay[displayId].width != w || mMultiDisplay[displayId].height != h) { 167 nativeResizeListener(displayId, w, h); 168 } 169 // only dpi changes 170 mMultiDisplay[displayId].virtualDisplay.resize(w, h, dpi); 171 mMultiDisplay[displayId].set(w, h, dpi, flag); 172 } 173 174 @Override run()175 public void run() { 176 while(nativeOpen() <= 0) { 177 Log.e(TAG, "failed to open multiDisplay pipe, retry"); 178 } 179 while(true) { 180 int[] array = {0, 0, 0, 0, 0, 0}; 181 if (!nativeReadPipe(array)) { 182 continue; 183 } 184 switch (array[0]) { 185 case ADD: { 186 for (int j = 0; j < 6; j++) { 187 Log.d(TAG, "received " + array[j]); 188 } 189 int i = array[1]; 190 int width = array[2]; 191 int height = array[3]; 192 int dpi = array[4]; 193 int flag = (array[5] != 0) ? array[5] : mFlags; 194 if (i < 1 || i > MAX_DISPLAYS || width <=0 || height <=0 || dpi <=0 195 || flag < 0) { 196 Log.e(TAG, "invalid parameters for add/modify display"); 197 break; 198 } 199 addVirtualDisplay(i, width, height, dpi, flag); 200 break; 201 } 202 case DEL: { 203 int i = array[1]; 204 Log.d(TAG, "DEL " + i); 205 if (i < 1 || i > MAX_DISPLAYS) { 206 Log.e(TAG, "invalid parameters for delete display"); 207 break; 208 } 209 deleteVirtualDisplay(i); 210 break; 211 } 212 } 213 } 214 } 215 } 216 nativeOpen()217 private native int nativeOpen(); nativeCreateSurface(int displayId, int width, int height)218 private native Surface nativeCreateSurface(int displayId, int width, int height); nativeReadPipe(int[] arr)219 private native boolean nativeReadPipe(int[] arr); nativeReleaseListener(int displayId)220 private native boolean nativeReleaseListener(int displayId); nativeResizeListener(int displayId, int with, int height)221 private native boolean nativeResizeListener(int displayId, int with, int height); 222 } 223