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