1 /*
2  * Copyright (C) 2022 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.activity;
18 
19 import static android.view.Display.DEFAULT_DISPLAY;
20 
21 import static org.junit.Assert.assertFalse;
22 import static org.junit.Assert.assertTrue;
23 import static org.junit.Assume.assumeTrue;
24 
25 import android.Manifest;
26 import android.os.Bundle;
27 import android.platform.test.annotations.Presubmit;
28 import android.server.wm.WindowManagerState;
29 import android.server.wm.WindowManagerTestBase;
30 
31 import com.android.compatibility.common.util.ApiTest;
32 
33 import org.junit.After;
34 import org.junit.Before;
35 import org.junit.Test;
36 
37 import java.util.concurrent.CountDownLatch;
38 import java.util.concurrent.Executors;
39 import java.util.concurrent.TimeUnit;
40 
41 /**
42  * Ensure Activity screencapture callback is invoked as expected.
43  *
44  * <p>Build/Install/Run: atest CtsWindowManagerDeviceActivity:ActivityCaptureCallbackTests
45  */
46 @Presubmit
47 @ApiTest(apis = {"android.app.Activity#registerScreenCaptureCallback",
48         "android.app.Activity#unregisterScreenCaptureCallback"})
49 public class ActivityCaptureCallbackTests extends WindowManagerTestBase {
50     private PrimaryActivity mPrimaryActivity;
51 
52     @Before
53     @Override
setUp()54     public void setUp() throws Exception {
55         super.setUp();
56         mPrimaryActivity = startActivity(PrimaryActivity.class, DEFAULT_DISPLAY);
57     }
58 
59     @After
tearDown()60     public void tearDown() throws Exception {
61         mInstrumentation.getUiAutomation().dropShellPermissionIdentity();
62     }
63 
64     /** Test a registered Activity callback invocation. */
65     @Test
testScreencaptureInvokeCallback()66     public void testScreencaptureInvokeCallback() {
67         mInstrumentation
68                 .getUiAutomation()
69                 .adoptShellPermissionIdentity(Manifest.permission.STATUS_BAR_SERVICE);
70         mWm.notifyScreenshotListeners(DEFAULT_DISPLAY);
71         mPrimaryActivity.waitAndAssertCallbackInvokedOnActivity();
72     }
73 
74     /** Test multi-window activities, both callbacks are invoked. */
75     @Test
testScreencaptureInvokeCallbackOnAllVisibleActivities()76     public void testScreencaptureInvokeCallbackOnAllVisibleActivities() {
77         assumeTrue(supportsMultiDisplay());
78 
79         final WindowManagerState.DisplayContent newDisplay =
80                 createManagedExternalDisplaySession().createVirtualDisplay();
81         final SecondaryActivity secondaryActivity =
82                 startActivity(SecondaryActivity.class, newDisplay.mId, /* hasFocus */ false);
83         mInstrumentation
84                 .getUiAutomation()
85                 .adoptShellPermissionIdentity(Manifest.permission.STATUS_BAR_SERVICE);
86         mWm.notifyScreenshotListeners(DEFAULT_DISPLAY);
87         mWm.notifyScreenshotListeners(newDisplay.mId);
88         mPrimaryActivity.waitAndAssertCallbackInvokedOnActivity();
89         secondaryActivity.waitAndAssertCallbackInvokedOnActivity();
90     }
91 
92     /** Test screenshotting only one display. */
93     @Test
testScreencaptureInvokeCallbackOnOneDisplay()94     public void testScreencaptureInvokeCallbackOnOneDisplay() {
95         assumeTrue(supportsMultiDisplay());
96 
97         final WindowManagerState.DisplayContent newDisplay =
98                 createManagedExternalDisplaySession().createVirtualDisplay();
99         final SecondaryActivity secondaryActivity =
100                 startActivity(SecondaryActivity.class, newDisplay.mId, /* hasFocus */ false);
101         mInstrumentation
102                 .getUiAutomation()
103                 .adoptShellPermissionIdentity(Manifest.permission.STATUS_BAR_SERVICE);
104         mWm.notifyScreenshotListeners(DEFAULT_DISPLAY);
105         mPrimaryActivity.waitAndAssertCallbackInvokedOnActivity();
106         secondaryActivity.waitAndAssertCallbackNotInvoked();
107     }
108     /** Test multi-window activities, only registered callback is invoked. */
109     @Test
testScreencaptureInvokeCallbackOnRegisteredVisibleActivities()110     public void testScreencaptureInvokeCallbackOnRegisteredVisibleActivities() {
111         assumeTrue(supportsMultiDisplay());
112 
113         mPrimaryActivity.unregisterScreencaptureCallback();
114         final WindowManagerState.DisplayContent newDisplay =
115                 createManagedExternalDisplaySession().createVirtualDisplay();
116         final SecondaryActivity secondaryActivity =
117                 startActivity(SecondaryActivity.class, newDisplay.mId, /* hasFocus */ false);
118         mInstrumentation
119                 .getUiAutomation()
120                 .adoptShellPermissionIdentity(Manifest.permission.STATUS_BAR_SERVICE);
121         mWm.notifyScreenshotListeners(DEFAULT_DISPLAY);
122         mWm.notifyScreenshotListeners(newDisplay.mId);
123         mPrimaryActivity.waitAndAssertCallbackNotInvoked();
124         secondaryActivity.waitAndAssertCallbackInvokedOnActivity();
125     }
126 
127     /** Test only the top activity callback is invoked */
128     @Test
testScreencaptureInvokeCallbackOnVisibleOnly()129     public void testScreencaptureInvokeCallbackOnVisibleOnly() {
130         final SecondaryActivity topActivity =
131                 startActivity(SecondaryActivity.class, DEFAULT_DISPLAY);
132         mInstrumentation
133                 .getUiAutomation()
134                 .adoptShellPermissionIdentity(Manifest.permission.STATUS_BAR_SERVICE);
135         mWm.notifyScreenshotListeners(DEFAULT_DISPLAY);
136         topActivity.waitAndAssertCallbackInvokedOnActivity();
137         mPrimaryActivity.waitAndAssertCallbackNotInvoked();
138     }
139 
140     /** Test unregister callback */
141     @Test
testScreencaptureUnregisterCallback()142     public void testScreencaptureUnregisterCallback() {
143         mInstrumentation
144                 .getUiAutomation()
145                 .adoptShellPermissionIdentity(Manifest.permission.STATUS_BAR_SERVICE);
146         mPrimaryActivity.unregisterScreencaptureCallback();
147         mWm.notifyScreenshotListeners(DEFAULT_DISPLAY);
148         mPrimaryActivity.waitAndAssertCallbackNotInvoked();
149     }
150 
151     public static class PrimaryActivity extends ScreencaptureCallbackActivity {}
152 
153     public static class SecondaryActivity extends ScreencaptureCallbackActivity {}
154 
155     private static class ScreencaptureCallbackActivity extends FocusableActivity {
156         private static final long TIMEOUT_SCREENCAPTURE_CALLBACK_INVOKED = 1000; // milliseconds
157 
158         private CountDownLatch mCountDownLatch = new CountDownLatch(1);
159 
160         final ScreenCaptureCallback mCallback =
161                 new ScreenCaptureCallback() {
162 
163                     @Override
164                     public void onScreenCaptured() {
165                         mCountDownLatch.countDown();
166                     }
167                 };
168 
waitAndAssertCallbackInvokedOnActivity()169         void waitAndAssertCallbackInvokedOnActivity() {
170             try {
171                 boolean invoked = mCountDownLatch.await(
172                         TIMEOUT_SCREENCAPTURE_CALLBACK_INVOKED, TimeUnit.MILLISECONDS);
173                 assertTrue(invoked);
174             } catch (InterruptedException e) {
175                 // This shouldn't happen
176             }
177         }
178 
waitAndAssertCallbackNotInvoked()179         void waitAndAssertCallbackNotInvoked() {
180             try {
181                 boolean invoked = mCountDownLatch.await(
182                         TIMEOUT_SCREENCAPTURE_CALLBACK_INVOKED, TimeUnit.MILLISECONDS);
183                 assertFalse(invoked);
184             } catch (InterruptedException e) {
185                 // This shouldn't happen
186             }
187         }
188 
unregisterScreencaptureCallback()189         public void unregisterScreencaptureCallback() {
190             unregisterScreenCaptureCallback(mCallback);
191         }
192 
193         @Override
onCreate(Bundle savedInstanceState)194         protected void onCreate(Bundle savedInstanceState) {
195             super.onCreate(savedInstanceState);
196             registerScreenCaptureCallback(Executors.newSingleThreadExecutor(), mCallback);
197         }
198     }
199 }
200