1 /*
2  * Copyright (C) 2021 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.cts.overlay.target;
18 
19 import static com.google.common.truth.Truth.assertThat;
20 import static com.google.common.truth.Truth.assertWithMessage;
21 
22 import static org.junit.Assert.fail;
23 
24 import android.app.Instrumentation;
25 import android.content.Intent;
26 import android.os.Bundle;
27 import android.os.UserHandle;
28 
29 import androidx.test.InstrumentationRegistry;
30 import androidx.test.rule.ActivityTestRule;
31 import androidx.test.runner.AndroidJUnit4;
32 
33 import com.android.compatibility.common.util.PollingCheck;
34 import com.android.compatibility.common.util.SystemUtil;
35 
36 import org.junit.Before;
37 import org.junit.Rule;
38 import org.junit.Test;
39 import org.junit.runner.RunWith;
40 
41 import java.util.concurrent.CountDownLatch;
42 import java.util.concurrent.TimeUnit;
43 
44 @RunWith(AndroidJUnit4.class)
45 public class OverlayTargetTest {
46     // overlay package
47     private static final String OVERLAY_ALL_PACKAGE_NAME = "com.android.cts.overlay.all";
48 
49     // Overlay states
50     private static final String STATE_DISABLED = "STATE_DISABLED";
51     private static final String STATE_ENABLED = "STATE_ENABLED";
52 
53     // Default timeout value
54     private static final long TIMEOUT_MS = TimeUnit.SECONDS.toMillis(5);
55 
56     // Keys for test arguments
57     private static final String PARAM_START_SERVICE = "start_service";
58 
59     private Instrumentation mInstrumentation;
60 
61     @Rule
62     public ActivityTestRule<OverlayTargetActivity> mActivityTestRule = new ActivityTestRule<>(
63             OverlayTargetActivity.class, false /* initialTouchMode */, false /* launchActivity */);
64 
65     @Before
setup()66     public void setup() {
67         mInstrumentation = InstrumentationRegistry.getInstrumentation();
68         launchOverlayTargetActivity(InstrumentationRegistry.getArguments());
69         assertThat(mActivityTestRule.getActivity()).isNotNull();
70     }
71 
72     @Test
overlayEnabled_activityInForeground()73     public void overlayEnabled_activityInForeground() throws Exception {
74         final OverlayTargetActivity targetActivity = mActivityTestRule.getActivity();
75         final CountDownLatch latch = new CountDownLatch(1);
76         targetActivity.setConfigurationChangedCallback((activity, config) -> {
77             latch.countDown();
78             activity.setConfigurationChangedCallback(null);
79         });
80 
81         setOverlayEnabled(OVERLAY_ALL_PACKAGE_NAME, true /* enabled */);
82 
83         if (!latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
84             fail("Fail to wait configuration changes for the overlay target activity.");
85         }
86     }
87 
88     @Test
overlayEnabled_activityInBackground_toForeground()89     public void overlayEnabled_activityInBackground_toForeground() throws Exception {
90         final OverlayTargetActivity targetActivity = mActivityTestRule.getActivity();
91         // Activity goes into background
92         launchSimpleActivity();
93         mInstrumentation.waitForIdleSync();
94         final CountDownLatch latch = new CountDownLatch(1);
95         targetActivity.setConfigurationChangedCallback((activity, config) -> {
96             latch.countDown();
97             activity.setConfigurationChangedCallback(null);
98         });
99         setOverlayEnabled(OVERLAY_ALL_PACKAGE_NAME, true /* enabled */);
100 
101         if (latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
102             fail("Activity in background should not receive configuration changes");
103         }
104 
105         // Bring activity to foreground
106         final Intent intent = new Intent(mInstrumentation.getTargetContext(),
107                 OverlayTargetActivity.class);
108         intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
109         targetActivity.startActivity(intent);
110 
111         if (!latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
112             fail("Fail to wait configuration changes for the overlay target activity.");
113         }
114     }
115 
launchOverlayTargetActivity(Bundle testArgs)116     private void launchOverlayTargetActivity(Bundle testArgs) {
117         final Intent intent = new Intent(mInstrumentation.getTargetContext(),
118                 OverlayTargetActivity.class);
119         final boolean startService = (testArgs != null
120                 && "true".equalsIgnoreCase(testArgs.getString(PARAM_START_SERVICE)));
121         intent.putExtra(OverlayTargetActivity.EXTRA_START_SERVICE, startService);
122         mActivityTestRule.launchActivity(intent);
123         mInstrumentation.waitForIdleSync();
124     }
125 
setOverlayEnabled(String overlayPackage, boolean enabled)126     private static void setOverlayEnabled(String overlayPackage, boolean enabled)
127             throws Exception {
128         final String current = getStateForOverlay(overlayPackage);
129         final String expected = enabled ? STATE_ENABLED : STATE_DISABLED;
130         assertThat(current).isNotEqualTo(expected);
131         SystemUtil.runShellCommand("cmd overlay "
132                 + (enabled ? "enable" : "disable")
133                 + " --user current "
134                 + overlayPackage);
135         PollingCheck.check("Fail to wait overlay enabled state " + expected
136                         + " for " + overlayPackage, TIMEOUT_MS,
137                 () -> expected.equals(getStateForOverlay(overlayPackage)));
138     }
139 
launchSimpleActivity()140     private void launchSimpleActivity() {
141         Intent intent = new Intent(Intent.ACTION_MAIN);
142         intent.setClass(mInstrumentation.getTargetContext(), SimpleActivity.class);
143         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
144         mInstrumentation.startActivitySync(intent);
145     }
146 
getStateForOverlay(String overlayPackage)147     private static String getStateForOverlay(String overlayPackage) {
148         final String errorMsg = "Fail to parse the state of overlay package " + overlayPackage;
149         final String result = SystemUtil.runShellCommand("cmd overlay dump");
150         final String overlayPackageForCurrentUser = overlayPackage + ":" + UserHandle.myUserId();
151         final int startIndex = result.indexOf(overlayPackageForCurrentUser);
152         assertWithMessage(errorMsg).that(startIndex).isAtLeast(0);
153 
154         final int endIndex = result.indexOf('}', startIndex);
155         assertWithMessage(errorMsg).that(endIndex).isGreaterThan(startIndex);
156 
157         final int stateIndex = result.indexOf("mState", startIndex);
158         assertWithMessage(errorMsg).that(startIndex).isLessThan(stateIndex);
159         assertWithMessage(errorMsg).that(stateIndex).isLessThan(endIndex);
160 
161         final int colonIndex = result.indexOf(':', stateIndex);
162         assertWithMessage(errorMsg).that(stateIndex).isLessThan(colonIndex);
163         assertWithMessage(errorMsg).that(colonIndex).isLessThan(endIndex);
164 
165         final int endLineIndex = result.indexOf('\n', colonIndex);
166         assertWithMessage(errorMsg).that(colonIndex).isLessThan(endLineIndex);
167         assertWithMessage(errorMsg).that(endLineIndex).isLessThan(endIndex);
168 
169         return result.substring(colonIndex + 2, endLineIndex);
170     }
171 }
172