xref: /aosp_15_r20/cts/tests/framework/base/windowmanager/app/src/android/server/wm/app/VirtualDisplayActivity.java (revision b7c941bb3fa97aba169d73cee0bed2de8ac964bf)
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