1 /* 2 * Copyright (C) 2016 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 android.server.wm.app; 18 19 import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY; 20 import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PRESENTATION; 21 import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC; 22 import static android.server.wm.ActivityLauncher.KEY_DISPLAY_ID; 23 import static android.server.wm.ActivityLauncher.KEY_LAUNCH_ACTIVITY; 24 import static android.server.wm.ActivityLauncher.KEY_NEW_TASK; 25 import static android.server.wm.ActivityLauncher.KEY_TARGET_COMPONENT; 26 import static android.server.wm.ComponentNameUtils.getActivityName; 27 import static android.server.wm.app.Components.VirtualDisplayActivity.COMMAND_CREATE_DISPLAY; 28 import static android.server.wm.app.Components.VirtualDisplayActivity.COMMAND_DESTROY_DISPLAY; 29 import static android.server.wm.app.Components.VirtualDisplayActivity.COMMAND_RESIZE_DISPLAY; 30 import static android.server.wm.app.Components.VirtualDisplayActivity.KEY_CAN_SHOW_WITH_INSECURE_KEYGUARD; 31 import static android.server.wm.app.Components.VirtualDisplayActivity.KEY_COMMAND; 32 import static android.server.wm.app.Components.VirtualDisplayActivity.KEY_COUNT; 33 import static android.server.wm.app.Components.VirtualDisplayActivity.KEY_DENSITY_DPI; 34 import static android.server.wm.app.Components.VirtualDisplayActivity.KEY_PRESENTATION_DISPLAY; 35 import static android.server.wm.app.Components.VirtualDisplayActivity.KEY_PUBLIC_DISPLAY; 36 import static android.server.wm.app.Components.VirtualDisplayActivity.KEY_RESIZE_DISPLAY; 37 import static android.server.wm.app.Components.VirtualDisplayActivity.KEY_SHOW_SYSTEM_DECORATIONS; 38 import static android.server.wm.app.Components.VirtualDisplayActivity.KEY_SUPPORTS_TOUCH; 39 import static android.server.wm.app.Components.VirtualDisplayActivity.VIRTUAL_DISPLAY_PREFIX; 40 41 import android.app.Activity; 42 import android.content.ComponentName; 43 import android.content.Intent; 44 import android.hardware.display.DisplayManager; 45 import android.hardware.display.VirtualDisplay; 46 import android.os.Bundle; 47 import android.server.wm.ActivityLauncher; 48 import android.util.Log; 49 import android.view.Surface; 50 import android.view.SurfaceHolder; 51 import android.view.SurfaceView; 52 import android.view.ViewGroup; 53 import android.widget.LinearLayout; 54 55 import java.util.HashMap; 56 57 /** 58 * Activity that is able to create and destroy a virtual display. 59 */ 60 public class VirtualDisplayActivity extends Activity implements SurfaceHolder.Callback { 61 private static final String TAG = VirtualDisplayActivity.class.getSimpleName(); 62 63 private static final int DEFAULT_DENSITY_DPI = 160; 64 65 // Container for details about a pending virtual display creation request. 66 private static class VirtualDisplayRequest { 67 final SurfaceView surfaceView; 68 final Bundle extras; 69 VirtualDisplayRequest(SurfaceView surfaceView, Bundle extras)70 VirtualDisplayRequest(SurfaceView surfaceView, Bundle extras) { 71 this.surfaceView = surfaceView; 72 this.extras = extras; 73 } 74 } 75 76 // Container to hold association between an active virtual display and surface view. 77 private static class VirtualDisplayEntry { 78 final VirtualDisplay display; 79 final SurfaceView surfaceView; 80 final boolean resizeDisplay; 81 final int density; 82 VirtualDisplayEntry(VirtualDisplay display, SurfaceView surfaceView, int density, boolean resizeDisplay)83 VirtualDisplayEntry(VirtualDisplay display, SurfaceView surfaceView, int density, 84 boolean resizeDisplay) { 85 this.display = display; 86 this.surfaceView = surfaceView; 87 this.density = density; 88 this.resizeDisplay = resizeDisplay; 89 } 90 } 91 92 private final HashMap<Surface, VirtualDisplayRequest> mPendingDisplayRequests = new HashMap<>(); 93 private final HashMap<Surface, VirtualDisplayEntry> mVirtualDisplays = new HashMap<>(); 94 private DisplayManager mDisplayManager; 95 96 @Override onCreate(Bundle savedInstanceState)97 protected void onCreate(Bundle savedInstanceState) { 98 super.onCreate(savedInstanceState); 99 setContentView(R.layout.virtual_display_layout); 100 101 mDisplayManager = (DisplayManager) getSystemService(DISPLAY_SERVICE); 102 } 103 104 @Override onNewIntent(Intent intent)105 protected void onNewIntent(Intent intent) { 106 super.onNewIntent(intent); 107 final Bundle extras = intent.getExtras(); 108 if (extras == null) { 109 return; 110 } 111 112 String command = extras.getString(KEY_COMMAND); 113 switch (command) { 114 case COMMAND_CREATE_DISPLAY: 115 createVirtualDisplay(extras); 116 break; 117 case COMMAND_DESTROY_DISPLAY: 118 destroyVirtualDisplays(); 119 break; 120 case COMMAND_RESIZE_DISPLAY: 121 resizeDisplay(); 122 break; 123 } 124 } 125 126 @Override onDestroy()127 protected void onDestroy() { 128 super.onDestroy(); 129 destroyVirtualDisplays(); 130 } 131 createVirtualDisplay(Bundle extras)132 private void createVirtualDisplay(Bundle extras) { 133 final int requestedCount = extras.getInt(KEY_COUNT, 1); 134 Log.d(TAG, "createVirtualDisplays. requested count:" + requestedCount); 135 136 for (int displayCount = 0; displayCount < requestedCount; ++displayCount) { 137 final ViewGroup root = findViewById(R.id.display_content); 138 final SurfaceView surfaceView = new SurfaceView(this); 139 surfaceView.setLayoutParams( 140 // Using WRAP_CONTENT & layout weight to allow multiple virtual displays shown. 141 new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, 142 LinearLayout.LayoutParams.WRAP_CONTENT, 1.0f)); 143 surfaceView.getHolder().addCallback(this); 144 mPendingDisplayRequests.put(surfaceView.getHolder().getSurface(), 145 new VirtualDisplayRequest(surfaceView, extras)); 146 root.addView(surfaceView); 147 } 148 } 149 destroyVirtualDisplays()150 private void destroyVirtualDisplays() { 151 Log.d(TAG, "destroyVirtualDisplays"); 152 final ViewGroup root = findViewById(android.R.id.content); 153 154 for (VirtualDisplayEntry entry : mVirtualDisplays.values()) { 155 Log.d(TAG, "destroying:" + entry.display); 156 entry.display.release(); 157 root.removeView(entry.surfaceView); 158 } 159 160 mPendingDisplayRequests.clear(); 161 mVirtualDisplays.clear(); 162 } 163 164 @Override surfaceCreated(SurfaceHolder surfaceHolder)165 public void surfaceCreated(SurfaceHolder surfaceHolder) { 166 final VirtualDisplayRequest entry = 167 mPendingDisplayRequests.remove(surfaceHolder.getSurface()); 168 169 if (entry == null) { 170 return; 171 } 172 173 final int densityDpi = entry.extras.getInt(KEY_DENSITY_DPI, DEFAULT_DENSITY_DPI); 174 final boolean resizeDisplay = entry.extras.getBoolean(KEY_RESIZE_DISPLAY); 175 final Surface surface = surfaceHolder.getSurface(); 176 177 // Initially, the surface will not have a set width or height so rely on the parent. 178 // This should be accurate with match parent on both params. 179 final int width = surfaceHolder.getSurfaceFrame().width(); 180 final int height = surfaceHolder.getSurfaceFrame().height(); 181 182 int flags = 0; 183 184 final boolean canShowWithInsecureKeyguard 185 = entry.extras.getBoolean(KEY_CAN_SHOW_WITH_INSECURE_KEYGUARD); 186 if (canShowWithInsecureKeyguard) { 187 flags |= 1 << 5; // VIRTUAL_DISPLAY_FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD 188 } 189 190 final boolean publicDisplay = entry.extras.getBoolean(KEY_PUBLIC_DISPLAY); 191 if (publicDisplay) { 192 flags |= VIRTUAL_DISPLAY_FLAG_PUBLIC | VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY; 193 } 194 195 final boolean showSystemDecorations = entry.extras.getBoolean(KEY_SHOW_SYSTEM_DECORATIONS); 196 if (showSystemDecorations) { 197 flags |= 1 << 9; // VIRTUAL_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS; 198 } 199 200 final boolean presentationDisplay = entry.extras.getBoolean(KEY_PRESENTATION_DISPLAY); 201 if (presentationDisplay) { 202 flags |= VIRTUAL_DISPLAY_FLAG_PRESENTATION; 203 } 204 205 final boolean supportsTouch = entry.extras.getBoolean(KEY_SUPPORTS_TOUCH); 206 if (supportsTouch) { 207 flags |= 1 << 6; // VIRTUAL_DISPLAY_FLAG_SUPPORTS_TOUCH 208 } 209 210 Log.d(TAG, "createVirtualDisplay: " + width + "x" + height + ", dpi: " + densityDpi 211 + ", canShowWithInsecureKeyguard=" + canShowWithInsecureKeyguard 212 + ", publicDisplay=" + publicDisplay + ", presentationDisplay=" 213 + presentationDisplay + ", supportsTouch= " + supportsTouch); 214 215 try { 216 VirtualDisplay virtualDisplay = mDisplayManager.createVirtualDisplay( 217 VIRTUAL_DISPLAY_PREFIX + mVirtualDisplays.size(), width, 218 height, densityDpi, surface, flags); 219 mVirtualDisplays.put(surface, 220 new VirtualDisplayEntry(virtualDisplay, entry.surfaceView, densityDpi, 221 resizeDisplay)); 222 } catch (IllegalArgumentException e) { 223 final ViewGroup root = findViewById(android.R.id.content); 224 // This is expected when trying to create show-when-locked public display. 225 root.removeView(entry.surfaceView); 226 } 227 } 228 229 @Override surfaceChanged(SurfaceHolder surfaceHolder, int format, int width, int height)230 public void surfaceChanged(SurfaceHolder surfaceHolder, int format, int width, int height) { 231 final VirtualDisplayEntry entry = mVirtualDisplays.get(surfaceHolder.getSurface()); 232 233 if (entry != null && entry.resizeDisplay) { 234 entry.display.resize(width, height, entry.density); 235 } 236 } 237 238 @Override surfaceDestroyed(SurfaceHolder surfaceHolder)239 public void surfaceDestroyed(SurfaceHolder surfaceHolder) { 240 } 241 242 /** Resize virtual display to half of the surface frame size. */ resizeDisplay()243 private void resizeDisplay() { 244 final VirtualDisplayEntry vd = (VirtualDisplayEntry) mVirtualDisplays.values().toArray()[0]; 245 final SurfaceHolder surfaceHolder = vd.surfaceView.getHolder(); 246 vd.display.resize(surfaceHolder.getSurfaceFrame().width() / 2, 247 surfaceHolder.getSurfaceFrame().height() / 2, vd.density); 248 } 249 launchActivity(ComponentName activityName, int displayId)250 private void launchActivity(ComponentName activityName, int displayId) { 251 final Bundle extras = new Bundle(); 252 extras.putBoolean(KEY_LAUNCH_ACTIVITY, true); 253 extras.putString(KEY_TARGET_COMPONENT, getActivityName(activityName)); 254 extras.putInt(KEY_DISPLAY_ID, displayId); 255 extras.putBoolean(KEY_NEW_TASK, true); 256 ActivityLauncher.launchActivityFromExtras(this, extras); 257 } 258 } 259