1 /* 2 * Copyright (C) 2020 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.autofill; 18 19 import static com.android.compatibility.common.util.ShellUtils.runShellCommand; 20 21 import android.perftests.utils.SettingsHelper; 22 import android.provider.Settings; 23 import android.util.Log; 24 25 import androidx.annotation.NonNull; 26 import androidx.annotation.Nullable; 27 import androidx.test.InstrumentationRegistry; 28 29 import com.android.compatibility.common.util.Timeout; 30 31 import org.junit.rules.TestWatcher; 32 import org.junit.runner.Description; 33 34 import java.util.concurrent.CountDownLatch; 35 import java.util.concurrent.TimeUnit; 36 37 /** 38 * Custom {@link TestWatcher} that does the setup and reset tasks for the tests. 39 */ 40 final class AutofillTestWatcher extends TestWatcher { 41 42 private static final String TAG = "AutofillTestWatcher"; 43 private static final long GENERIC_TIMEOUT_MS = 10_000; 44 45 private static ServiceWatcher sServiceWatcher; 46 47 private String mOriginalLogLevel; 48 49 @Override starting(Description description)50 protected void starting(Description description) { 51 super.starting(description); 52 final String testName = description.getDisplayName(); 53 Log.i(TAG, "Starting " + testName); 54 55 prepareDevice(); 56 enableVerboseLog(); 57 // Prepare the service before each test. 58 // Disable the current AutofillService. 59 resetAutofillService(); 60 // Set MyAutofillService status enable, it can start to accept the calls. 61 enableMyAutofillService(); 62 setServiceWatcher(); 63 } 64 65 @Override finished(Description description)66 protected void finished(Description description) { 67 super.finished(description); 68 final String testName = description.getDisplayName(); 69 Log.i(TAG, "Finished " + testName); 70 restoreLogLevel(); 71 // Set MyAutofillService status disable, so the calls are ignored. 72 disableMyAutofillService(); 73 clearServiceWatcher(); 74 } 75 waitServiceConnect()76 void waitServiceConnect() throws InterruptedException { 77 if (sServiceWatcher != null) { 78 Log.d(TAG, "waitServiceConnect()"); 79 sServiceWatcher.waitOnConnected(); 80 } 81 } 82 83 /** 84 * Uses the {@code settings} binary to set the autofill service. 85 */ setAutofillService()86 void setAutofillService() { 87 String serviceName = MyAutofillService.COMPONENT_NAME; 88 SettingsHelper.syncSet(InstrumentationRegistry.getTargetContext(), 89 SettingsHelper.NAMESPACE_SECURE, 90 Settings.Secure.AUTOFILL_SERVICE, 91 serviceName); 92 // Waits until the service is actually enabled. 93 Timeout timeout = new Timeout("CONNECTION_TIMEOUT", GENERIC_TIMEOUT_MS, 2F, 94 GENERIC_TIMEOUT_MS); 95 try { 96 timeout.run("Enabling Autofill service", () -> { 97 return isAutofillServiceEnabled(serviceName) ? serviceName : null; 98 }); 99 } catch (Exception e) { 100 throw new AssertionError("Enabling Autofill service failed."); 101 } 102 } 103 104 /** 105 * Uses the {@code settings} binary to reset the autofill service. 106 */ resetAutofillService()107 void resetAutofillService() { 108 SettingsHelper.syncDelete(InstrumentationRegistry.getTargetContext(), 109 SettingsHelper.NAMESPACE_SECURE, 110 Settings.Secure.AUTOFILL_SERVICE); 111 } 112 113 /** 114 * Checks whether the given service is set as the autofill service for the default user. 115 */ isAutofillServiceEnabled(String serviceName)116 private boolean isAutofillServiceEnabled(String serviceName) { 117 String actualName = SettingsHelper.get(SettingsHelper.NAMESPACE_SECURE, 118 Settings.Secure.AUTOFILL_SERVICE); 119 return serviceName.equals(actualName); 120 } 121 prepareDevice()122 private void prepareDevice() { 123 // Unlock screen. 124 runShellCommand("input keyevent KEYCODE_WAKEUP"); 125 126 // Dismiss keyguard, in case it's set as "Swipe to unlock". 127 runShellCommand("wm dismiss-keyguard"); 128 129 // Collapse notifications. 130 runShellCommand("cmd statusbar collapse"); 131 } 132 enableMyAutofillService()133 private void enableMyAutofillService() { 134 MyAutofillService.resetStaticState(); 135 MyAutofillService.setEnabled(true); 136 } 137 disableMyAutofillService()138 private void disableMyAutofillService() { 139 // Must disable service so calls are ignored in case of errors during the test case; 140 // otherwise, other tests will fail because these calls are made in the UI thread (as both 141 // the service, the tests, and the app run in the same process). 142 MyAutofillService.setEnabled(false); 143 } 144 enableVerboseLog()145 private void enableVerboseLog() { 146 mOriginalLogLevel = runShellCommand("cmd autofill get log_level"); 147 Log.d(TAG, "enableVerboseLog(), mOriginalLogLevel=" + mOriginalLogLevel); 148 if (!mOriginalLogLevel.equals("verbose")) { 149 runShellCommand("cmd autofill set log_level verbose"); 150 } 151 } 152 restoreLogLevel()153 private void restoreLogLevel() { 154 Log.d(TAG, "restoreLogLevel to " + mOriginalLogLevel); 155 if (!mOriginalLogLevel.equals("verbose")) { 156 runShellCommand("cmd autofill set log_level %s", mOriginalLogLevel); 157 } 158 } 159 setServiceWatcher()160 private static void setServiceWatcher() { 161 if (sServiceWatcher == null) { 162 sServiceWatcher = new ServiceWatcher(); 163 } 164 } 165 clearServiceWatcher()166 private static void clearServiceWatcher() { 167 if (sServiceWatcher != null) { 168 sServiceWatcher = null; 169 } 170 } 171 172 public static final class ServiceWatcher { 173 private final CountDownLatch mConnected = new CountDownLatch(1); 174 onConnected()175 public static void onConnected() { 176 Log.i(TAG, "onConnected: sServiceWatcher=" + sServiceWatcher); 177 178 sServiceWatcher.mConnected.countDown(); 179 } 180 181 @NonNull waitOnConnected()182 public void waitOnConnected() throws InterruptedException { 183 await(mConnected, "not connected"); 184 } 185 await(@onNull CountDownLatch latch, @NonNull String fmt, @Nullable Object... args)186 private void await(@NonNull CountDownLatch latch, @NonNull String fmt, 187 @Nullable Object... args) 188 throws InterruptedException { 189 final boolean called = latch.await(GENERIC_TIMEOUT_MS, TimeUnit.MILLISECONDS); 190 if (!called) { 191 throw new IllegalStateException(String.format(fmt, args) 192 + " in " + GENERIC_TIMEOUT_MS + "ms"); 193 } 194 } 195 } 196 } 197