• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2024 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.view.inputmethod.cts.installtests;
18 
19 import static com.android.bedstead.multiuser.MultiUserDeviceStateExtensionsKt.additionalUser;
20 
21 import static org.junit.Assert.assertTrue;
22 
23 import android.Manifest;
24 import android.content.ComponentName;
25 import android.content.Context;
26 import android.text.TextUtils;
27 import android.view.inputmethod.InputMethodInfo;
28 import android.view.inputmethod.InputMethodManager;
29 
30 import androidx.annotation.NonNull;
31 import androidx.test.filters.LargeTest;
32 import androidx.test.platform.app.InstrumentationRegistry;
33 
34 import com.android.bedstead.harrier.BedsteadJUnit4;
35 import com.android.bedstead.harrier.DeviceState;
36 import com.android.bedstead.multiuser.annotations.EnsureHasAdditionalUser;
37 import com.android.bedstead.multiuser.annotations.RequireMultiUserSupport;
38 import com.android.bedstead.nene.TestApis;
39 import com.android.bedstead.nene.users.UserReference;
40 import com.android.compatibility.common.util.PollingCheck;
41 import com.android.compatibility.common.util.SystemUtil;
42 
43 import org.junit.After;
44 import org.junit.ClassRule;
45 import org.junit.Rule;
46 import org.junit.Test;
47 import org.junit.runner.RunWith;
48 
49 import java.io.File;
50 import java.util.List;
51 import java.util.Objects;
52 import java.util.concurrent.Callable;
53 import java.util.concurrent.TimeUnit;
54 import java.util.function.Predicate;
55 
56 @LargeTest
57 @RequireMultiUserSupport
58 @RunWith(BedsteadJUnit4.class)
59 public final class UserUnlockTest {
60     private static final long TIMEOUT = TimeUnit.SECONDS.toMillis(15);
61 
62     private static final ComponentName DIRECT_BOOT_AWARE_IME = ComponentName.createRelative(
63             "com.android.cts.directbootawareime", ".DirectBootAwareIme");
64     private static final String DIRECT_BOOT_AWARE_IME_ID =
65             DIRECT_BOOT_AWARE_IME.flattenToShortString();
66 
67     private static final ComponentName DIRECT_BOOT_UNAWARE_IME = ComponentName.createRelative(
68             "com.android.cts.directbootawareime", ".DirectBootUnawareIme");
69     private static final String DIRECT_BOOT_UNAWARE_IME_ID =
70             DIRECT_BOOT_UNAWARE_IME.flattenToShortString();
71 
72     private static final String TEST_APK_PACKAGE_NAME = DIRECT_BOOT_AWARE_IME.getPackageName();
73     private static final File TEST_APK_PACKAGE_FILE =
74             new File("/data/local/tmp/cts/inputmethod/CtsDirectBootAwareIme.apk");
75 
76     @ClassRule
77     @Rule
78     public static final DeviceState sDeviceState = new DeviceState();  // Required by Bedstead.
79 
80     @After
tearDown()81     public void tearDown() {
82         TestApis.packages().find(TEST_APK_PACKAGE_NAME).uninstallFromAllUsers();
83     }
84 
getInputMethodListAsUser( @onNull InputMethodManager imm, int userId)85     private static List<InputMethodInfo> getInputMethodListAsUser(
86             @NonNull InputMethodManager imm, int userId) {
87         return SystemUtil.runWithShellPermissionIdentity(
88                 () -> imm.getInputMethodListAsUser(userId),
89                 Manifest.permission.INTERACT_ACROSS_USERS_FULL);
90     }
91 
imeIdMatcher(@onNull String imeId)92     private static Predicate<InputMethodInfo> imeIdMatcher(@NonNull String imeId) {
93         return imi -> TextUtils.equals(imi.getId(), imeId);
94     }
95 
assertImeInApiResult(@onNull InputMethodManager imm, @NonNull String imeId, int userId, long timeout)96     private static void assertImeInApiResult(@NonNull InputMethodManager imm,
97             @NonNull String imeId, int userId, long timeout) throws Exception {
98         pollingCheck("Ime " + imeId + " must exist.", timeout, () ->
99                 getInputMethodListAsUser(imm, userId).stream().anyMatch(imeIdMatcher(imeId)));
100     }
101 
assertImeNotInApiResult(@onNull InputMethodManager imm, @NonNull String imeId, int userId, long timeout)102     private static void assertImeNotInApiResult(@NonNull InputMethodManager imm,
103             @NonNull String imeId, int userId, long timeout) throws Exception {
104         pollingCheck("Ime " + imeId + " must not exist.", timeout, () ->
105                 getInputMethodListAsUser(imm, userId).stream().noneMatch(imeIdMatcher(imeId)));
106     }
107 
pollingCheck(@onNull String message, long timeout, @NonNull Callable<Boolean> condition)108     private static void pollingCheck(@NonNull String message, long timeout,
109             @NonNull Callable<Boolean> condition) throws Exception {
110         if (timeout <= 0) {
111             assertTrue(message, condition.call());
112         } else {
113             PollingCheck.check(message, timeout, condition);
114         }
115     }
116 
117     /**
118      * A regression test for b/356037588.
119      */
120     @Test
121     @EnsureHasAdditionalUser
testDirectBootUnawareImesInvisibleAfterStoppingUser()122     public void testDirectBootUnawareImesInvisibleAfterStoppingUser() throws Exception {
123         final Context context = InstrumentationRegistry.getInstrumentation().getContext();
124         final InputMethodManager imm = Objects.requireNonNull(
125                 context.getSystemService(InputMethodManager.class));
126 
127         final UserReference additionalUser = additionalUser(sDeviceState);
128         final int additionalUserId = additionalUser.id();
129         assertTrue(additionalUser.isUnlocked());
130 
131         // Install the test IME
132         TestApis.packages().install(additionalUser, TEST_APK_PACKAGE_FILE);
133 
134         // When the user is unlocked, both direct-boot aware/unaware IMEs should be visible.
135         assertImeInApiResult(imm, DIRECT_BOOT_AWARE_IME_ID, additionalUserId, TIMEOUT);
136         assertImeInApiResult(imm, DIRECT_BOOT_UNAWARE_IME_ID, additionalUserId, 0 /* timeout */);
137 
138         try {
139             // Stopping the user makes the user storage be locked again.
140             additionalUser.stop();
141 
142             PollingCheck.check("Waiting for the user=" + additionalUserId + " to be locked",
143                     TIMEOUT, () -> !additionalUser.isUnlocked());
144 
145             // When the user is unlocked, only direct-boot aware IME should be visible.
146             assertImeNotInApiResult(imm, DIRECT_BOOT_UNAWARE_IME_ID, additionalUserId, TIMEOUT);
147             assertImeInApiResult(imm, DIRECT_BOOT_AWARE_IME_ID, additionalUserId, 0 /* timeout */);
148         } finally {
149             // The test would get stuck if the additional user is not running upon existing.
150             if (!additionalUser.isRunning()) {
151                 additionalUser.start();
152             }
153         }
154     }
155 }
156