1 /*
2  * Copyright (C) 2017 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 package com.android.cts.launchertests;
17 
18 import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.setDefaultLauncher;
19 
20 import static junit.framework.Assert.assertFalse;
21 import static junit.framework.Assert.assertNotNull;
22 import static junit.framework.Assert.assertNull;
23 import static junit.framework.Assert.assertTrue;
24 import static junit.framework.Assert.fail;
25 
26 import static org.testng.Assert.assertEquals;
27 import static org.testng.Assert.assertThrows;
28 
29 import android.content.BroadcastReceiver;
30 import android.content.ComponentName;
31 import android.content.Context;
32 import android.content.Intent;
33 import android.os.Bundle;
34 import android.os.UserHandle;
35 import android.os.UserManager;
36 import android.support.test.uiautomator.UiDevice;
37 import android.text.TextUtils;
38 
39 import androidx.test.InstrumentationRegistry;
40 import androidx.test.runner.AndroidJUnit4;
41 
42 import com.android.compatibility.common.util.BlockingBroadcastReceiver;
43 
44 import org.junit.After;
45 import org.junit.Before;
46 import org.junit.Test;
47 import org.junit.runner.RunWith;
48 
49 import java.util.Arrays;
50 import java.util.Set;
51 import java.util.concurrent.LinkedBlockingQueue;
52 import java.util.concurrent.TimeUnit;
53 import java.util.regex.Matcher;
54 import java.util.regex.Pattern;
55 import java.util.stream.Collectors;
56 
57 /**
58  * Test that runs {@link UserManager#trySetQuietModeEnabled(boolean, UserHandle)} API
59  * against valid target user.
60  */
61 @RunWith(AndroidJUnit4.class)
62 public class QuietModeTest {
63     private static final String PARAM_TARGET_USER = "TARGET_USER";
64     private static final String PARAM_ORIGINAL_DEFAULT_LAUNCHER = "ORIGINAL_DEFAULT_LAUNCHER";
65     private static final ComponentName LAUNCHER_ACTIVITY =
66             new ComponentName(
67                     "com.android.cts.launchertests.support",
68                     "com.android.cts.launchertests.support.LauncherActivity");
69 
70     private static final ComponentName COMMAND_RECEIVER =
71             new ComponentName(
72                     "com.android.cts.launchertests.support",
73                     "com.android.cts.launchertests.support.QuietModeCommandReceiver");
74     private static final String EXTRA_FLAGS = "quiet_mode_flags";
75 
76     private UserManager mUserManager;
77     private UserHandle mTargetUser;
78     private Context mContext;
79     private String mOriginalLauncher;
80     private UiDevice mUiDevice;
81 
82     @Before
setupUserManager()83     public void setupUserManager() throws Exception {
84         mContext = InstrumentationRegistry.getTargetContext();
85         mUserManager = mContext.getSystemService(UserManager.class);
86     }
87 
88     @Before
readParams()89     public void readParams() {
90         Context context = InstrumentationRegistry.getContext();
91         Bundle arguments = InstrumentationRegistry.getArguments();
92         UserManager userManager = context.getSystemService(UserManager.class);
93         final int userSn = Integer.parseInt(arguments.getString(PARAM_TARGET_USER));
94         mTargetUser = userManager.getUserForSerialNumber(userSn);
95         mOriginalLauncher = arguments.getString(PARAM_ORIGINAL_DEFAULT_LAUNCHER);
96     }
97 
98     @Before
wakeupDeviceAndUnlock()99     public void wakeupDeviceAndUnlock() throws Exception {
100         mUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
101         mUiDevice.wakeUp();
102         mUiDevice.pressMenu();
103     }
104 
105     @Before
106     @After
revertToDefaultLauncher()107     public void revertToDefaultLauncher() throws Exception {
108         if (TextUtils.isEmpty(mOriginalLauncher)) {
109             return;
110         }
111         setDefaultLauncher(InstrumentationRegistry.getInstrumentation(), mOriginalLauncher);
112     }
113 
114     @Test
testTryEnableQuietMode_defaultForegroundLauncher()115     public void testTryEnableQuietMode_defaultForegroundLauncher() throws Exception {
116         setTestAppAsDefaultLauncher();
117         startLauncherActivityInTestApp();
118 
119         Intent intent = trySetQuietModeEnabled(true);
120         assertNotNull("Failed to receive ACTION_MANAGED_PROFILE_UNAVAILABLE broadcast", intent);
121         assertTrue(mUserManager.isQuietModeEnabled(mTargetUser));
122 
123         intent = trySetQuietModeEnabled(false);
124         assertNotNull("Failed to receive ACTION_MANAGED_PROFILE_AVAILABLE broadcast", intent);
125         assertFalse(mUserManager.isQuietModeEnabled(mTargetUser));
126     }
127 
128     @Test
testTryEnableQuietMode_notForegroundLauncher()129     public void testTryEnableQuietMode_notForegroundLauncher() throws InterruptedException {
130         setTestAppAsDefaultLauncher();
131 
132         assertThrows(SecurityException.class, () -> trySetQuietModeEnabled(true));
133         assertFalse(mUserManager.isQuietModeEnabled(mTargetUser));
134     }
135 
136     @Test
testTryEnableQuietMode_notDefaultLauncher()137     public void testTryEnableQuietMode_notDefaultLauncher() throws Exception {
138         startLauncherActivityInTestApp();
139 
140         assertThrows(SecurityException.class, () -> trySetQuietModeEnabled(true));
141         assertFalse(mUserManager.isQuietModeEnabled(mTargetUser));
142     }
143 
144     @Test
testTryEnableQuietMode_noCredentialRequest()145     public void testTryEnableQuietMode_noCredentialRequest() throws Exception {
146         setTestAppAsDefaultLauncher();
147         startLauncherActivityInTestApp();
148 
149         Intent intent = trySetQuietModeEnabled(true,
150                 UserManager.QUIET_MODE_DISABLE_ONLY_IF_CREDENTIAL_NOT_REQUIRED, true);
151         assertNotNull("Failed to receive ACTION_MANAGED_PROFILE_UNAVAILABLE broadcast", intent);
152         assertTrue(mUserManager.isQuietModeEnabled(mTargetUser));
153 
154         waitForUserLocked();
155 
156         intent = trySetQuietModeEnabled(false,
157                 UserManager.QUIET_MODE_DISABLE_ONLY_IF_CREDENTIAL_NOT_REQUIRED, false);
158         assertNull("Received ACTION_MANAGED_PROFILE_AVAILABLE broadcast", intent);
159         assertTrue(mUserManager.isQuietModeEnabled(mTargetUser));
160     }
161 
waitForUserLocked()162     private void waitForUserLocked() throws Exception {
163         // Should match a line in "dumpsys mount" output like this:
164         // Local unlocked users: [0, 10]
165         final Pattern p = Pattern.compile("Local unlocked users: \\[(.*)\\]");
166         final long deadline = System.nanoTime() + TimeUnit.SECONDS.toNanos(60);
167         while (System.nanoTime() < deadline) {
168             final String output = mUiDevice.executeShellCommand("dumpsys mount");
169             final Matcher matcher = p.matcher(output);
170             assertTrue("Unexpected dupmsys mount output: " + output, matcher.find());
171             final Set<Integer> unlockedUsers = Arrays.stream(matcher.group(1).split(", "))
172                     .map(Integer::valueOf)
173                     .collect(Collectors.toSet());
174             if (!unlockedUsers.contains(mTargetUser.getIdentifier())) {
175                 return;
176             }
177             Thread.sleep(500);
178         }
179         fail("Cannot get the profile locked");
180     }
181 
trySetQuietModeEnabled(boolean enabled, int flags, boolean expectsCredentialsNotNeeded)182     private Intent trySetQuietModeEnabled(boolean enabled, int flags,
183             boolean expectsCredentialsNotNeeded) throws Exception {
184         return trySetQuietModeEnabled(enabled, true, flags, expectsCredentialsNotNeeded);
185     }
186 
187     @Test
testTryEnableQuietMode()188     public void testTryEnableQuietMode() throws Exception {
189         setTestAppAsDefaultLauncher();
190         startLauncherActivityInTestApp();
191 
192         Intent intent = trySetQuietModeEnabled(true);
193         assertNotNull("Failed to receive ACTION_MANAGED_PROFILE_UNAVAILABLE broadcast", intent);
194         assertTrue(mUserManager.isQuietModeEnabled(mTargetUser));
195     }
196 
197     @Test
testTryDisableQuietMode()198     public void testTryDisableQuietMode() throws Exception {
199         setTestAppAsDefaultLauncher();
200         startLauncherActivityInTestApp();
201 
202         Intent intent = trySetQuietModeEnabled(false);
203         assertNotNull("Failed to receive ACTION_MANAGED_PROFILE_AVAILABLE broadcast", intent);
204         assertFalse(mUserManager.isQuietModeEnabled(mTargetUser));
205     }
206 
trySetQuietModeEnabled(boolean enabled)207     private Intent trySetQuietModeEnabled(boolean enabled) throws Exception {
208         return trySetQuietModeEnabled(enabled, false, 0, true);
209     }
210 
trySetQuietModeEnabled(boolean enabled, boolean hasFlags, int flags, boolean expectsCredentialsNotNeeded)211     private Intent trySetQuietModeEnabled(boolean enabled, boolean hasFlags, int flags,
212             boolean expectsCredentialsNotNeeded) throws Exception {
213         final String action = enabled
214                 ? Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE
215                 : Intent.ACTION_MANAGED_PROFILE_AVAILABLE;
216 
217         BlockingBroadcastReceiver receiver =
218                 new BlockingBroadcastReceiver(mContext, action);
219         try {
220             receiver.register();
221 
222             boolean credentialNotNeeded = askLauncherSupportAppToSetQuietMode(enabled, hasFlags,
223                     flags);
224             assertEquals(credentialNotNeeded, expectsCredentialsNotNeeded);
225             return receiver.awaitForBroadcast();
226         } finally {
227             receiver.unregisterQuietly();
228         }
229     }
230 
231     /**
232      * Ask launcher support test app to set quiet mode by sending broadcast.
233      * <p>
234      * We cannot simply make this package the launcher and call the API because instrumentation
235      * process would always considered to be in the foreground. The trick here is to send
236      * broadcast to another test app which is launcher itself and call the API through it.
237      * The receiver will then send back the result, and it should be either true, false or
238      * security-exception.
239      * <p>
240      * All the constants defined here should be aligned with
241      * com.android.cts.launchertests.support.QuietModeCommandReceiver.
242      */
askLauncherSupportAppToSetQuietMode(boolean enabled, boolean hasFlags, int flags)243     private boolean askLauncherSupportAppToSetQuietMode(boolean enabled, boolean hasFlags, int flags) throws Exception {
244         Intent intent = new Intent("toggle_quiet_mode");
245         intent.setComponent(COMMAND_RECEIVER);
246         intent.putExtra("quiet_mode", enabled);
247         intent.putExtra(Intent.EXTRA_USER, mTargetUser);
248         if (hasFlags) {
249             intent.putExtra(EXTRA_FLAGS, flags);
250         }
251 
252         // Ask launcher support app to set quiet mode by sending broadcast.
253         LinkedBlockingQueue<String> blockingQueue = new LinkedBlockingQueue<>();
254         mContext.sendOrderedBroadcast(intent, null, new BroadcastReceiver() {
255             @Override
256             public void onReceive(Context context, Intent intent) {
257                 blockingQueue.offer(getResultData());
258             }
259         }, null, 0, "", null);
260 
261         // Wait for the result.
262         String result = null;
263         for (int i = 0; i < 10; i++) {
264             // Broadcast won't be delivered when the device is sleeping, so wake up the device
265             // in between each attempt.
266             wakeupDeviceAndUnlock();
267             result = blockingQueue.poll(10, TimeUnit.SECONDS);
268             if (!TextUtils.isEmpty(result)) {
269                 break;
270             }
271         }
272 
273         // Parse the result.
274         assertNotNull(result);
275         if ("true".equalsIgnoreCase(result)) {
276             return true;
277         } else if ("false".equalsIgnoreCase(result)) {
278             return false;
279         } else if ("security-exception".equals(result)) {
280             throw new SecurityException();
281         }
282         throw new IllegalStateException("Unexpected result : " + result);
283     }
284 
startActivitySync(String activity)285     private void startActivitySync(String activity) throws Exception {
286         mUiDevice.executeShellCommand("am start -W -n " + activity);
287     }
288 
289     /**
290      * Start the launcher activity in the test app to make it foreground.
291      */
startLauncherActivityInTestApp()292     private void startLauncherActivityInTestApp() throws Exception {
293         startActivitySync(LAUNCHER_ACTIVITY.flattenToString());
294     }
295 
setTestAppAsDefaultLauncher()296     private void setTestAppAsDefaultLauncher() {
297         setDefaultLauncher(InstrumentationRegistry.getInstrumentation(),
298                 LAUNCHER_ACTIVITY.getPackageName());
299     }
300 }
301 
302