1 /* 2 * Copyright (C) 2018 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 android.contentcaptureservice.cts; 17 18 import static com.android.compatibility.common.util.ShellUtils.runShellCommand; 19 20 import android.app.Activity; 21 import android.contentcaptureservice.cts.CtsContentCaptureService.Session; 22 import android.os.Bundle; 23 import android.util.Log; 24 import android.view.View; 25 import android.view.contentcapture.ContentCaptureManager; 26 27 import androidx.annotation.NonNull; 28 import androidx.annotation.Nullable; 29 30 import java.util.concurrent.Callable; 31 import java.util.concurrent.CountDownLatch; 32 import java.util.concurrent.TimeUnit; 33 import java.util.concurrent.atomic.AtomicReference; 34 35 /** 36 * Base class for all activities. 37 */ 38 abstract class AbstractContentCaptureActivity extends Activity { 39 40 private final String mTag = getClass().getSimpleName(); 41 42 private int mRealTaskId; 43 44 @Nullable getContentCaptureManager()45 public ContentCaptureManager getContentCaptureManager() { 46 return getSystemService(ContentCaptureManager.class); 47 } 48 49 @Override onCreate(Bundle savedInstanceState)50 protected void onCreate(Bundle savedInstanceState) { 51 mRealTaskId = getTaskId(); 52 Log.d(mTag, "onCreate(): taskId=" + mRealTaskId + ", decorView=" + getDecorView()); 53 54 super.onCreate(savedInstanceState); 55 } 56 57 @Override onStart()58 protected void onStart() { 59 Log.d(mTag, "onStart()"); 60 super.onStart(); 61 } 62 63 @Override onResume()64 protected void onResume() { 65 Log.d(mTag, "onResume(): decorViewId=" + getDecorView().getAutofillId()); 66 super.onResume(); 67 } 68 69 @Override onPause()70 protected void onPause() { 71 Log.d(mTag, "onPause()"); 72 super.onPause(); 73 } 74 75 @Override onStop()76 protected void onStop() { 77 Log.d(mTag, "onStop()"); 78 super.onStop(); 79 } 80 81 @Override onDestroy()82 protected void onDestroy() { 83 Log.d(mTag, "onDestroy()"); 84 super.onDestroy(); 85 } 86 87 @NonNull getDecorView()88 public final View getDecorView() { 89 return getWindow().getDecorView(); 90 } 91 92 /** 93 * Asserts the events generated when this session was launched and finished, 94 * without any custom / dynamic operations in between. 95 */ assertDefaultEvents(@onNull Session session)96 public abstract void assertDefaultEvents(@NonNull Session session); 97 98 /** 99 * Gets the real task id associated with the activity, as {@link #getTaskId()} returns 100 * {@code -1} after it's gone. 101 */ getRealTaskId()102 public final int getRealTaskId() { 103 return mRealTaskId; 104 } 105 106 /** 107 * Runs an action in the UI thread, and blocks caller until the action is finished. 108 */ syncRunOnUiThread(@onNull Runnable action)109 public final void syncRunOnUiThread(@NonNull Runnable action) { 110 syncRunOnUiThread(action, Helper.GENERIC_TIMEOUT_MS); 111 } 112 113 /** 114 * Calls an action in the UI thread, and blocks caller until the action is finished. 115 */ syncCallOnUiThread(@onNull Callable<T> action)116 public final <T> T syncCallOnUiThread(@NonNull Callable<T> action) throws Exception { 117 final AtomicReference<T> result = new AtomicReference<>(); 118 final AtomicReference<Exception> exception = new AtomicReference<>(); 119 syncRunOnUiThread(() -> { 120 try { 121 result.set(action.call()); 122 } catch (Exception e) { 123 exception.set(e); 124 } 125 }); 126 final Exception e = exception.get(); 127 if (e != null) { 128 throw e; 129 } 130 return result.get(); 131 } 132 133 /** 134 * Run an action in the UI thread, and blocks caller until the action is finished or it times 135 * out. 136 */ syncRunOnUiThread(@onNull Runnable action, long timeoutMs)137 public final void syncRunOnUiThread(@NonNull Runnable action, long timeoutMs) { 138 final CountDownLatch latch = new CountDownLatch(1); 139 runOnUiThread(() -> { 140 action.run(); 141 latch.countDown(); 142 }); 143 try { 144 if (!latch.await(timeoutMs, TimeUnit.MILLISECONDS)) { 145 // TODO(b/120665995): throw RetryableException (once moved from Autofill to common) 146 throw new IllegalStateException( 147 String.format("action on UI thread timed out after %d ms", timeoutMs)); 148 } 149 } catch (InterruptedException e) { 150 Thread.currentThread().interrupt(); 151 throw new RuntimeException("Interrupted", e); 152 } 153 } 154 155 /** 156 * Dumps the {@link ContentCaptureManager} state of the activity on logcat. 157 */ dumpIt()158 public void dumpIt() { 159 final String dump = runShellCommand( 160 "dumpsys activity %s --contentcapture", getComponentName().flattenToString()); 161 Log.v(mTag, "dump it: " + dump); 162 } 163 } 164