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