Merge "Made ContentCapture APIs available for testing."
diff --git a/api/current.txt b/api/current.txt
index 9dec22e..50ff701 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -44136,7 +44136,7 @@
}
public final class AvailableNetworkInfo implements android.os.Parcelable {
- ctor public AvailableNetworkInfo(int, int, java.util.ArrayList<java.lang.String>);
+ ctor public AvailableNetworkInfo(int, int, java.util.List<java.lang.String>);
method public int describeContents();
method public java.util.List<java.lang.String> getMccMncs();
method public int getPriority();
@@ -45098,7 +45098,7 @@
method public int getNetworkType();
method public int getPhoneCount();
method public int getPhoneType();
- method public int getPreferredOpportunisticDataSubscription();
+ method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PHONE_STATE}) public int getPreferredOpportunisticDataSubscription();
method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public android.telephony.ServiceState getServiceState();
method @Nullable public android.telephony.SignalStrength getSignalStrength();
method public int getSimCarrierId();
@@ -45134,6 +45134,7 @@
method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_NETWORK_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isDataRoamingEnabled();
method public boolean isHearingAidCompatibilitySupported();
method public boolean isNetworkRoaming();
+ method public boolean isRttSupported();
method public boolean isSmsCapable();
method @Deprecated public boolean isTtyModeSupported();
method public boolean isVoiceCapable();
@@ -45666,9 +45667,9 @@
}
public interface GroupCallCallback {
- method public void onBroadcastSignalStrengthUpdated(@IntRange(from=0xffffffff, to=4) int);
- method public void onError(int, @Nullable String);
- method public void onGroupCallStateChanged(int, int);
+ method public default void onBroadcastSignalStrengthUpdated(@IntRange(from=0xffffffff, to=4) int);
+ method public default void onError(int, @Nullable String);
+ method public default void onGroupCallStateChanged(int, int);
field public static final int SIGNAL_STRENGTH_UNAVAILABLE = -1; // 0xffffffff
}
@@ -45726,10 +45727,10 @@
}
public interface MbmsGroupCallSessionCallback {
- method public void onAvailableSaisUpdated(@NonNull java.util.List<java.lang.Integer>, @NonNull java.util.List<java.util.List<java.lang.Integer>>);
- method public void onError(int, @Nullable String);
- method public void onMiddlewareReady();
- method public void onServiceInterfaceAvailable(@NonNull String, int);
+ method public default void onAvailableSaisUpdated(@NonNull java.util.List<java.lang.Integer>, @NonNull java.util.List<java.util.List<java.lang.Integer>>);
+ method public default void onError(int, @Nullable String);
+ method public default void onMiddlewareReady();
+ method public default void onServiceInterfaceAvailable(@NonNull String, int);
}
public class MbmsStreamingSessionCallback {
@@ -50668,7 +50669,7 @@
method public void setClickable(boolean);
method public void setClipBounds(android.graphics.Rect);
method public void setClipToOutline(boolean);
- method public void setContentCaptureSession(@NonNull android.view.contentcapture.ContentCaptureSession);
+ method public void setContentCaptureSession(@Nullable android.view.contentcapture.ContentCaptureSession);
method public void setContentDescription(CharSequence);
method public void setContextClickable(boolean);
method public void setDefaultFocusHighlightEnabled(boolean);
diff --git a/api/system-current.txt b/api/system-current.txt
index 87c8c70..a9ef144 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -9298,6 +9298,8 @@
method @Nullable public android.view.contentcapture.ViewNode getViewNode();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.view.contentcapture.ContentCaptureEvent> CREATOR;
+ field public static final int TYPE_INITIAL_VIEW_TREE_APPEARED = 5; // 0x5
+ field public static final int TYPE_INITIAL_VIEW_TREE_APPEARING = 4; // 0x4
field public static final int TYPE_VIEW_APPEARED = 1; // 0x1
field public static final int TYPE_VIEW_DISAPPEARED = 2; // 0x2
field public static final int TYPE_VIEW_TEXT_CHANGED = 3; // 0x3
diff --git a/api/test-current.txt b/api/test-current.txt
index 32b131f..d434129 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -1769,6 +1769,7 @@
method @RequiresPermission(android.Manifest.permission.CLEAR_APP_USER_DATA) public static long getContributedMediaSize(android.content.Context, String, android.os.UserHandle) throws java.io.IOException;
method @NonNull public static java.io.File getVolumePath(@NonNull String) throws java.io.FileNotFoundException;
method @NonNull public static java.util.Collection<java.io.File> getVolumeScanPaths(@NonNull String) throws java.io.FileNotFoundException;
+ field public static final String EXTRA_ORIGINATED_FROM_SHELL = "android.intent.extra.originated_from_shell";
field public static final String SCAN_FILE_CALL = "scan_file";
field public static final String SCAN_VOLUME_CALL = "scan_volume";
}
@@ -2164,7 +2165,6 @@
public class TelephonyManager {
method public int checkCarrierPrivilegesForPackage(String);
method public int getCarrierIdListVersion();
- method public boolean isRttSupported();
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void refreshUiccProfile();
method public void setCarrierTestOverride(String, String, String, String, String, String, String);
field public static final int CARRIER_PRIVILEGE_STATUS_ERROR_LOADING_RULES = -2; // 0xfffffffe
diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp
index eaba9be..40a4070 100644
--- a/cmds/statsd/src/logd/LogEvent.cpp
+++ b/cmds/statsd/src/logd/LogEvent.cpp
@@ -559,7 +559,7 @@
// Handles the oneof field in KeyValuePair atom.
if (isKeyValuePairAtom && depth == 2) {
- pos[depth] = 4;
+ pos[depth] = 5;
}
mValues.push_back(FieldValue(Field(mTagId, pos, depth), Value(elem.data.float32)));
@@ -575,7 +575,7 @@
// Handles the oneof field in KeyValuePair atom.
if (isKeyValuePairAtom && depth == 2) {
- pos[depth] = 3;
+ pos[depth] = 4;
}
mValues.push_back(FieldValue(Field(mTagId, pos, depth),
Value(string(elem.data.string, elem.len))));
@@ -593,7 +593,7 @@
}
// Handles the oneof field in KeyValuePair atom.
if (isKeyValuePairAtom && depth == 2) {
- pos[depth] = 2;
+ pos[depth] = 3;
}
mValues.push_back(
FieldValue(Field(mTagId, pos, depth), Value((int64_t)elem.data.int64)));
diff --git a/cmds/statsd/tests/LogEvent_test.cpp b/cmds/statsd/tests/LogEvent_test.cpp
index 3a5be43..eec3c73 100644
--- a/cmds/statsd/tests/LogEvent_test.cpp
+++ b/cmds/statsd/tests/LogEvent_test.cpp
@@ -155,7 +155,7 @@
EXPECT_EQ(33, item5.mValue.int_value);
const FieldValue& item6 = event1.getValues()[6];
- EXPECT_EQ(0x2010482, item6.mField.getField());
+ EXPECT_EQ(0x2010483, item6.mField.getField());
EXPECT_EQ(Type::LONG, item6.mValue.getType());
EXPECT_EQ(678L, item6.mValue.int_value);
@@ -165,7 +165,7 @@
EXPECT_EQ(44, item7.mValue.int_value);
const FieldValue& item8 = event1.getValues()[8];
- EXPECT_EQ(0x2010582, item8.mField.getField());
+ EXPECT_EQ(0x2010583, item8.mField.getField());
EXPECT_EQ(Type::LONG, item8.mValue.getType());
EXPECT_EQ(890L, item8.mValue.int_value);
@@ -175,7 +175,7 @@
EXPECT_EQ(1, item9.mValue.int_value);
const FieldValue& item10 = event1.getValues()[10];
- EXPECT_EQ(0x2010683, item10.mField.getField());
+ EXPECT_EQ(0x2010684, item10.mField.getField());
EXPECT_EQ(Type::STRING, item10.mValue.getType());
EXPECT_EQ("test2", item10.mValue.str_value);
@@ -185,7 +185,7 @@
EXPECT_EQ(2, item11.mValue.int_value);
const FieldValue& item12 = event1.getValues()[12];
- EXPECT_EQ(0x2010783, item12.mField.getField());
+ EXPECT_EQ(0x2010784, item12.mField.getField());
EXPECT_EQ(Type::STRING, item12.mValue.getType());
EXPECT_EQ("test1", item12.mValue.str_value);
@@ -195,7 +195,7 @@
EXPECT_EQ(111, item13.mValue.int_value);
const FieldValue& item14 = event1.getValues()[14];
- EXPECT_EQ(0x2010884, item14.mField.getField());
+ EXPECT_EQ(0x2010885, item14.mField.getField());
EXPECT_EQ(Type::FLOAT, item14.mValue.getType());
EXPECT_EQ(2.2f, item14.mValue.float_value);
@@ -205,7 +205,7 @@
EXPECT_EQ(222, item15.mValue.int_value);
const FieldValue& item16 = event1.getValues()[16];
- EXPECT_EQ(0x2018984, item16.mField.getField());
+ EXPECT_EQ(0x2018985, item16.mField.getField());
EXPECT_EQ(Type::FLOAT, item16.mValue.getType());
EXPECT_EQ(1.1f, item16.mValue.float_value);
}
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index aac8f08..0eadd1d 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -7155,7 +7155,8 @@
mInstrumentation.onEnterAnimationComplete();
onEnterAnimationComplete();
if (getWindow() != null && getWindow().getDecorView() != null) {
- getWindow().getDecorView().getViewTreeObserver().dispatchOnEnterAnimationComplete();
+ View decorView = getWindow().getDecorView();
+ decorView.getViewTreeObserver().dispatchOnEnterAnimationComplete();
}
}
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 9ee2f03..ea145f0 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -4485,6 +4485,7 @@
* @hide
*/
public int noteProxyOpNoThrow(int op, String proxiedPackageName) {
+ logOperationIfNeeded(op, mContext.getOpPackageName(), proxiedPackageName);
try {
return mService.noteProxyOperation(op, Process.myUid(), mContext.getOpPackageName(),
Binder.getCallingUid(), proxiedPackageName);
@@ -4500,7 +4501,7 @@
*/
@UnsupportedAppUsage
public int noteOpNoThrow(int op, int uid, String packageName) {
- logNoteOpIfNeeded(op, packageName);
+ logOperationIfNeeded(op, packageName, null);
try {
return mService.noteOperation(op, uid, packageName);
} catch (RemoteException e) {
@@ -4608,6 +4609,7 @@
* @hide
*/
public int startOpNoThrow(int op, int uid, String packageName, boolean startIfModeDefault) {
+ logOperationIfNeeded(op, packageName, null);
try {
return mService.startOperation(getToken(mService), op, uid, packageName,
startIfModeDefault);
@@ -4624,6 +4626,7 @@
* @hide
*/
public void finishOp(int op, int uid, String packageName) {
+ logOperationIfNeeded(op, packageName, null);
try {
mService.finishOperation(getToken(mService), op, uid, packageName);
} catch (RemoteException e) {
@@ -4870,7 +4873,7 @@
return AppOpsManager.MODE_DEFAULT;
}
- private static void logNoteOpIfNeeded(int op, String callingPackage) {
+ private static void logOperationIfNeeded(int op, String callingPackage, String proxiedPackage) {
// Check if debug logging propety is enabled.
if (!SystemProperties.getBoolean(DEBUG_LOGGING_ENABLE_PROP, false)) {
return;
@@ -4908,6 +4911,6 @@
// Log a stack trace
Exception here = new Exception("HERE!");
android.util.Log.i(DEBUG_LOGGING_TAG, "Note operation package= " + callingPackage
- + " op= " + opStr, here);
+ + " proxied= " + proxiedPackage + " op= " + opStr, here);
}
}
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index e2907e2..6e52b33 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -866,6 +866,14 @@
*/
public static final int INSTALL_ENABLE_ROLLBACK = 0x00040000;
+ /**
+ * Flag parameter for {@link #installPackage} to indicate that package verification should be
+ * disabled for this package.
+ *
+ * @hide
+ */
+ public static final int INSTALL_DISABLE_VERIFICATION = 0x00080000;
+
/** @hide */
@IntDef(flag = true, prefix = { "DONT_KILL_APP" }, value = {
DONT_KILL_APP
diff --git a/core/java/android/ddm/DdmHandleHello.java b/core/java/android/ddm/DdmHandleHello.java
index b2288fc..87568e8 100644
--- a/core/java/android/ddm/DdmHandleHello.java
+++ b/core/java/android/ddm/DdmHandleHello.java
@@ -16,13 +16,15 @@
package android.ddm;
+import android.os.Debug;
+import android.os.UserHandle;
+import android.util.Log;
+
+import dalvik.system.VMRuntime;
+
import org.apache.harmony.dalvik.ddmc.Chunk;
import org.apache.harmony.dalvik.ddmc.ChunkHandler;
import org.apache.harmony.dalvik.ddmc.DdmServer;
-import android.util.Log;
-import android.os.Debug;
-import android.os.UserHandle;
-import dalvik.system.VMRuntime;
import java.nio.ByteBuffer;
@@ -35,6 +37,8 @@
public static final int CHUNK_WAIT = type("WAIT");
public static final int CHUNK_FEAT = type("FEAT");
+ private static final int CLIENT_PROTOCOL_VERSION = 1;
+
private static DdmHandleHello mInstance = new DdmHandleHello();
private static final String[] FRAMEWORK_FEATURES = new String[] {
@@ -145,7 +149,7 @@
+ vmFlags.length() * 2
+ 1);
out.order(ChunkHandler.CHUNK_ORDER);
- out.putInt(DdmServer.CLIENT_PROTOCOL_VERSION);
+ out.putInt(CLIENT_PROTOCOL_VERSION);
out.putInt(android.os.Process.myPid());
out.putInt(vmIdent.length());
out.putInt(appName.length());
diff --git a/core/java/android/os/AsyncTask.java b/core/java/android/os/AsyncTask.java
index 141d33b..1f33693 100644
--- a/core/java/android/os/AsyncTask.java
+++ b/core/java/android/os/AsyncTask.java
@@ -19,6 +19,7 @@
import android.annotation.MainThread;
import android.annotation.Nullable;
import android.annotation.WorkerThread;
+
import java.util.ArrayDeque;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Callable;
@@ -70,7 +71,7 @@
* protected Long doInBackground(URL... urls) {
* int count = urls.length;
* long totalSize = 0;
- * for (int i = 0; i < count; i++) {
+ * for (int i = 0; i < count; i++) {
* totalSize += Downloader.downloadFile(urls[i]);
* publishProgress((int) ((i / (float) count) * 100));
* // Escape early if cancel() is called
@@ -158,13 +159,22 @@
* </ul>
*
* <h2>Memory observability</h2>
- * <p>AsyncTask guarantees that all callback calls are synchronized in such a way that the following
- * operations are safe without explicit synchronizations.</p>
+ * <p>AsyncTask guarantees that all callback calls are synchronized to ensure the following
+ * without explicit synchronizations.</p>
* <ul>
- * <li>Set member fields in the constructor or {@link #onPreExecute}, and refer to them
- * in {@link #doInBackground}.
- * <li>Set member fields in {@link #doInBackground}, and refer to them in
- * {@link #onProgressUpdate} and {@link #onPostExecute}.
+ * <li>The memory effects of {@link #onPreExecute}, and anything else
+ * executed before the call to {@link #execute}, including the construction
+ * of the AsyncTask object, are visible to {@link #doInBackground}.
+ * <li>The memory effects of {@link #doInBackground} are visible to
+ * {@link #onPostExecute}.
+ * <li>Any memory effects of {@link #doInBackground} preceding a call
+ * to {@link #publishProgress} are visible to the corresponding
+ * {@link #onProgressUpdate} call. (But {@link #doInBackground} continues to
+ * run, and care needs to be taken that later updates in {@link #doInBackground}
+ * do not interfere with an in-progress {@link #onProgressUpdate} call.)
+ * <li>Any memory effects preceding a call to {@link #cancel} are visible
+ * after a call to {@link #isCancelled} that returns true as a result, or
+ * during and after a resulting call to {@link #onCancelled}.
* </ul>
*
* <h2>Order of execution</h2>
@@ -388,6 +398,10 @@
* specified parameters are the parameters passed to {@link #execute}
* by the caller of this task.
*
+ * This will normally run on a background thread. But to better
+ * support testing frameworks, it is recommended that this also tolerates
+ * direct execution on the foreground thread, as part of the {@link #execute} call.
+ *
* This method can call {@link #publishProgress} to publish updates
* on the UI thread.
*
@@ -404,6 +418,8 @@
/**
* Runs on the UI thread before {@link #doInBackground}.
+ * Invoked directly by {@link #execute} or {@link #executeOnExecutor}.
+ * The default version does nothing.
*
* @see #onPostExecute
* @see #doInBackground
@@ -414,7 +430,10 @@
/**
* <p>Runs on the UI thread after {@link #doInBackground}. The
- * specified result is the value returned by {@link #doInBackground}.</p>
+ * specified result is the value returned by {@link #doInBackground}.
+ * To better support testing frameworks, it is recommended that this be
+ * written to tolerate direct execution as part of the execute() call.
+ * The default version does nothing.</p>
*
* <p>This method won't be invoked if the task was cancelled.</p>
*
@@ -432,6 +451,7 @@
/**
* Runs on the UI thread after {@link #publishProgress} is invoked.
* The specified values are the values passed to {@link #publishProgress}.
+ * The default version does nothing.
*
* @param values The values indicating progress.
*
@@ -466,7 +486,8 @@
/**
* <p>Applications should preferably override {@link #onCancelled(Object)}.
* This method is invoked by the default implementation of
- * {@link #onCancelled(Object)}.</p>
+ * {@link #onCancelled(Object)}.
+ * The default version does nothing.</p>
*
* <p>Runs on the UI thread after {@link #cancel(boolean)} is invoked and
* {@link #doInBackground(Object[])} has finished.</p>
@@ -504,12 +525,16 @@
* an attempt to stop the task.</p>
*
* <p>Calling this method will result in {@link #onCancelled(Object)} being
- * invoked on the UI thread after {@link #doInBackground(Object[])}
- * returns. Calling this method guarantees that {@link #onPostExecute(Object)}
- * is never invoked. After invoking this method, you should check the
- * value returned by {@link #isCancelled()} periodically from
- * {@link #doInBackground(Object[])} to finish the task as early as
- * possible.</p>
+ * invoked on the UI thread after {@link #doInBackground(Object[])} returns.
+ * Calling this method guarantees that onPostExecute(Object) is never
+ * subsequently invoked, even if <tt>cancel</tt> returns false, but
+ * {@link #onPostExecute} has not yet run. To finish the
+ * task as early as possible, check {@link #isCancelled()} periodically from
+ * {@link #doInBackground(Object[])}.</p>
+ *
+ * <p>This only requests cancellation. It never waits for a running
+ * background task to terminate, even if <tt>mayInterruptIfRunning</tt> is
+ * true.</p>
*
* @param mayInterruptIfRunning <tt>true</tt> if the thread executing this
* task should be interrupted; otherwise, in-progress tasks are allowed
diff --git a/core/java/android/os/BugreportManager.java b/core/java/android/os/BugreportManager.java
index 3a5b8a8..27f7e22 100644
--- a/core/java/android/os/BugreportManager.java
+++ b/core/java/android/os/BugreportManager.java
@@ -24,7 +24,8 @@
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.content.Context;
-import android.os.IBinder.DeathRecipient;
+
+import com.android.internal.util.Preconditions;
import java.io.FileDescriptor;
import java.lang.annotation.Retention;
@@ -127,13 +128,16 @@
@NonNull BugreportParams params,
@NonNull @CallbackExecutor Executor executor,
@NonNull BugreportCallback callback) {
- // TODO(b/111441001): Enforce android.Manifest.permission.DUMP if necessary.
+ Preconditions.checkNotNull(bugreportFd);
+ Preconditions.checkNotNull(params);
+ Preconditions.checkNotNull(executor);
+ Preconditions.checkNotNull(callback);
DumpstateListener dsListener = new DumpstateListener(executor, callback);
try {
// Note: mBinder can get callingUid from the binder transaction.
mBinder.startBugreport(-1 /* callingUid */,
mContext.getOpPackageName(),
- (bugreportFd != null ? bugreportFd.getFileDescriptor() : new FileDescriptor()),
+ bugreportFd.getFileDescriptor(),
(screenshotFd != null
? screenshotFd.getFileDescriptor() : new FileDescriptor()),
params.getMode(), dsListener);
@@ -154,8 +158,7 @@
}
}
- private final class DumpstateListener extends IDumpstateListener.Stub
- implements DeathRecipient {
+ private final class DumpstateListener extends IDumpstateListener.Stub {
private final Executor mExecutor;
private final BugreportCallback mCallback;
@@ -165,11 +168,6 @@
}
@Override
- public void binderDied() {
- // TODO(b/111441001): implement
- }
-
- @Override
public void onProgress(int progress) throws RemoteException {
final long identity = Binder.clearCallingIdentity();
try {
diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java
index 0743c23..67c8400 100644
--- a/core/java/android/provider/MediaStore.java
+++ b/core/java/android/provider/MediaStore.java
@@ -110,6 +110,16 @@
public static final String SCAN_VOLUME_CALL = "scan_volume";
/**
+ * Extra used with {@link #SCAN_FILE_CALL} or {@link #SCAN_VOLUME_CALL} to indicate that
+ * the file path originated from shell.
+ *
+ * {@hide}
+ */
+ @TestApi
+ public static final String EXTRA_ORIGINATED_FROM_SHELL =
+ "android.intent.extra.originated_from_shell";
+
+ /**
* The method name used by the media scanner and mtp to tell the media provider to
* rescan and reclassify that have become unhidden because of renaming folders or
* removing nomedia files
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 992b996..0295dc8 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -82,6 +82,7 @@
import android.sysprop.DisplayProperties;
import android.text.InputType;
import android.text.TextUtils;
+import android.util.ArrayMap;
import android.util.AttributeSet;
import android.util.FloatProperty;
import android.util.LayoutDirection;
@@ -126,7 +127,6 @@
import android.widget.ScrollBarDrawable;
import com.android.internal.R;
-import com.android.internal.util.Preconditions;
import com.android.internal.view.TooltipPopup;
import com.android.internal.view.menu.MenuBuilder;
import com.android.internal.widget.ScrollBarUtils;
@@ -813,6 +813,8 @@
*/
private static final String CONTENT_CAPTURE_LOG_TAG = "View.ContentCapture";
+ private static final boolean DEBUG_CONTENT_CAPTURE = false;
+
/**
* When set to true, this view will save its attribute data.
*
@@ -3393,9 +3395,12 @@
* Masks for mPrivateFlags4, as generated by dumpFlags():
*
* |-------|-------|-------|-------|
- * 1111 PFLAG4_IMPORTANT_FOR_CONTENT_CAPTURE_MASK
- * 1 PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED
- * 1 PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED
+ * 1111 PFLAG4_IMPORTANT_FOR_CONTENT_CAPTURE_MASK
+ * 1 PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED
+ * 1 PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED
+ * 1 PFLAG4_CONTENT_CAPTURE_IMPORTANCE_IS_CACHED
+ * 1 PFLAG4_CONTENT_CAPTURE_IMPORTANCE_CACHED_VALUE
+ * 11 PFLAG4_CONTENT_CAPTURE_IMPORTANCE_MASK
* |-------|-------|-------|-------|
*/
@@ -3422,6 +3427,17 @@
private static final int PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED = 0x10;
private static final int PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED = 0x20;
+ /*
+ * Flags used to cache the value returned by isImportantForContentCapture while the view
+ * hierarchy is being traversed.
+ */
+ private static final int PFLAG4_CONTENT_CAPTURE_IMPORTANCE_IS_CACHED = 0x40;
+ private static final int PFLAG4_CONTENT_CAPTURE_IMPORTANCE_CACHED_VALUE = 0x80;
+
+ private static final int PFLAG4_CONTENT_CAPTURE_IMPORTANCE_MASK =
+ PFLAG4_CONTENT_CAPTURE_IMPORTANCE_IS_CACHED
+ | PFLAG4_CONTENT_CAPTURE_IMPORTANCE_CACHED_VALUE;
+
/* End of masks for mPrivateFlags4 */
/** @hide */
@@ -4140,9 +4156,13 @@
* The layout parameters associated with this view and used by the parent
* {@link android.view.ViewGroup} to determine how this view should be
* laid out.
+ *
+ * The field should not be used directly. Instead {@link #getLayoutParams()} and {@link
+ * #setLayoutParams(ViewGroup.LayoutParams)} should be used. The setter guarantees internal
+ * state correctness of the class.
* {@hide}
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
protected ViewGroup.LayoutParams mLayoutParams;
/**
@@ -5042,12 +5062,17 @@
* view (through {@link #setContentCaptureSession(ContentCaptureSession)}.
*/
@Nullable
- private WeakReference<ContentCaptureSession> mContentCaptureSession;
+ private ContentCaptureSession mContentCaptureSession;
@LayoutRes
private int mSourceLayoutId = ID_NULL;
/**
+ * Cached reference to the {@link ContentCaptureSession}, is reset on {@link #invalidate()}.
+ */
+ private ContentCaptureSession mCachedContentCaptureSession;
+
+ /**
* Simple constructor to use when creating a view from code.
*
* @param context The Context the view is running in, through which it can
@@ -8233,15 +8258,7 @@
* </ul>
*/
public void onProvideContentCaptureStructure(@NonNull ViewStructure structure, int flags) {
- if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
- Trace.traceBegin(Trace.TRACE_TAG_VIEW,
- "onProvideContentCaptureStructure() for " + getClass().getSimpleName());
- }
- try {
- onProvideStructure(structure, VIEW_STRUCTURE_FOR_CONTENT_CAPTURE, flags);
- } finally {
- Trace.traceEnd(Trace.TRACE_TAG_VIEW);
- }
+ onProvideStructure(structure, VIEW_STRUCTURE_FOR_CONTENT_CAPTURE, flags);
}
/** @hide */
@@ -8952,6 +8969,27 @@
* @see #IMPORTANT_FOR_CONTENT_CAPTURE_NO_EXCLUDE_DESCENDANTS
*/
public final boolean isImportantForContentCapture() {
+ boolean isImportant;
+ if ((mPrivateFlags4 & PFLAG4_CONTENT_CAPTURE_IMPORTANCE_IS_CACHED) != 0) {
+ isImportant = (mPrivateFlags4 & PFLAG4_CONTENT_CAPTURE_IMPORTANCE_CACHED_VALUE) != 0;
+ return isImportant;
+ }
+
+ isImportant = calculateIsImportantForContentCapture();
+
+ mPrivateFlags4 &= ~PFLAG4_CONTENT_CAPTURE_IMPORTANCE_CACHED_VALUE;
+ if (isImportant) {
+ mPrivateFlags4 |= PFLAG4_CONTENT_CAPTURE_IMPORTANCE_CACHED_VALUE;
+ }
+ mPrivateFlags4 |= PFLAG4_CONTENT_CAPTURE_IMPORTANCE_IS_CACHED;
+ return isImportant;
+ }
+
+ /**
+ * Calculates whether the flag is important for content capture so it can be used by
+ * {@link #isImportantForContentCapture()} while the tree is traversed.
+ */
+ private boolean calculateIsImportantForContentCapture() {
// Check parent mode to ensure we're important
ViewParent parent = mParent;
while (parent instanceof View) {
@@ -8992,9 +9030,6 @@
}
// View group is important if at least one children also is
- //TODO(b/111276913): decide if we really need to send the relevant parents or just the
- // leaves (with absolute coordinates). If it's the latter, then we need to update this
- // javadoc and ViewGroup's implementation.
if (this instanceof ViewGroup) {
final ViewGroup group = (ViewGroup) this;
for (int i = 0; i < group.getChildCount(); i++) {
@@ -9031,6 +9066,10 @@
* </ol>
*/
private void notifyAppearedOrDisappearedForContentCaptureIfNeeded(boolean appeared) {
+ AttachInfo ai = mAttachInfo;
+ // Skip it while the view is being laided out for the first time
+ if (ai != null && !ai.mReadyForContentCaptureUpdates) return;
+
if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
Trace.traceBegin(Trace.TRACE_TAG_VIEW,
"notifyContentCapture(" + appeared + ") for " + getClass().getSimpleName());
@@ -9043,24 +9082,27 @@
}
private void notifyAppearedOrDisappearedForContentCaptureIfNeededNoTrace(boolean appeared) {
+ AttachInfo ai = mAttachInfo;
+
// First check if context has client, so it saves a service lookup when it doesn't
if (!mContext.isContentCaptureSupported()) return;
// Then check if it's enabled in the context...
- final ContentCaptureManager ccm = mContext.getSystemService(ContentCaptureManager.class);
+ final ContentCaptureManager ccm = ai != null ? ai.getContentCaptureManager(mContext)
+ : mContext.getSystemService(ContentCaptureManager.class);
if (ccm == null || !ccm.isContentCaptureEnabled()) return;
// ... and finally at the view level
// NOTE: isImportantForContentCapture() is more expensive than cm.isContentCaptureEnabled()
if (!isImportantForContentCapture()) return;
- final ContentCaptureSession session = getContentCaptureSession(ccm);
+ ContentCaptureSession session = getContentCaptureSession();
if (session == null) return;
if (appeared) {
if (!isLaidOut() || getVisibility() != VISIBLE
|| (mPrivateFlags4 & PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED) != 0) {
- if (Log.isLoggable(CONTENT_CAPTURE_LOG_TAG, Log.VERBOSE)) {
+ if (DEBUG_CONTENT_CAPTURE) {
Log.v(CONTENT_CAPTURE_LOG_TAG, "Ignoring 'appeared' on " + this + ": laid="
+ isLaidOut() + ", visibleToUser=" + isVisibleToUser()
+ ", visible=" + (getVisibility() == VISIBLE)
@@ -9071,8 +9113,12 @@
}
return;
}
- mPrivateFlags4 |= PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED;
- mPrivateFlags4 &= ~PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED;
+ setNotifiedContentCaptureAppeared();
+
+ // TODO(b/123307965): instead of post, we should queue it on AttachInfo and then
+ // dispatch on RootImpl, as we're doing with the removed ones (in that case, we should
+ // merge the delayNotifyContentCaptureDisappeared() into a more generic method that
+ // takes a session and a command, where the command is either view added or removed
// The code below doesn't take much for a unique view, but it's called for all views
// the first time the view hiearchy is laid off, which could acccumulative delay the
@@ -9086,7 +9132,7 @@
} else {
if ((mPrivateFlags4 & PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED) == 0
|| (mPrivateFlags4 & PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED) != 0) {
- if (Log.isLoggable(CONTENT_CAPTURE_LOG_TAG, Log.VERBOSE)) {
+ if (DEBUG_CONTENT_CAPTURE) {
Log.v(CONTENT_CAPTURE_LOG_TAG, "Ignoring 'disappeared' on " + this + ": laid="
+ isLaidOut() + ", visibleToUser=" + isVisibleToUser()
+ ", visible=" + (getVisibility() == VISIBLE)
@@ -9099,11 +9145,24 @@
}
mPrivateFlags4 |= PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED;
mPrivateFlags4 &= ~PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED;
- Choreographer.getInstance().postCallback(Choreographer.CALLBACK_COMMIT,
- () -> session.notifyViewDisappeared(getAutofillId()), /* token= */ null);
+
+ if (ai != null) {
+ ai.delayNotifyContentCaptureDisappeared(session, getAutofillId());
+ } else {
+ if (DEBUG_CONTENT_CAPTURE) {
+ Log.v(CONTENT_CAPTURE_LOG_TAG, "no AttachInfo on gone for " + this);
+ }
+ Choreographer.getInstance().postCallback(Choreographer.CALLBACK_COMMIT,
+ () -> session.notifyViewDisappeared(getAutofillId()), /* token= */ null);
+ }
}
}
+ private void setNotifiedContentCaptureAppeared() {
+ mPrivateFlags4 |= PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED;
+ mPrivateFlags4 &= ~PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED;
+ }
+
/**
* Sets the (optional) {@link ContentCaptureSession} associated with this view.
*
@@ -9127,9 +9186,8 @@
* {@link ContentCaptureSession#createContentCaptureSession(
* android.view.contentcapture.ContentCaptureContext)}.
*/
- public void setContentCaptureSession(@NonNull ContentCaptureSession contentCaptureSession) {
- mContentCaptureSession = new WeakReference<>(
- Preconditions.checkNotNull(contentCaptureSession));
+ public void setContentCaptureSession(@Nullable ContentCaptureSession contentCaptureSession) {
+ mContentCaptureSession = contentCaptureSession;
}
/**
@@ -9141,8 +9199,18 @@
*/
@Nullable
public final ContentCaptureSession getContentCaptureSession() {
+ if (mCachedContentCaptureSession != null) {
+ return mCachedContentCaptureSession;
+ }
+
+ mCachedContentCaptureSession = getAndCacheContentCaptureSession();
+ return mCachedContentCaptureSession;
+ }
+
+ @Nullable
+ private ContentCaptureSession getAndCacheContentCaptureSession() {
// First try the session explicitly set by setContentCaptureSession()
- if (mContentCaptureSession != null) return mContentCaptureSession.get();
+ if (mContentCaptureSession != null) return mContentCaptureSession;
// Then the session explicitly set in an ancestor
ContentCaptureSession session = null;
@@ -9159,21 +9227,6 @@
return session;
}
- /**
- * Optimized version of {@link #getContentCaptureSession()} that avoids a service lookup.
- */
- @Nullable
- private ContentCaptureSession getContentCaptureSession(@NonNull ContentCaptureManager ccm) {
- if (mContentCaptureSession != null) return mContentCaptureSession.get();
-
- ContentCaptureSession session = null;
- if (mParent instanceof View) {
- session = ((View) mParent).getContentCaptureSession(ccm);
- }
-
- return session != null ? session : ccm.getMainContentCaptureSession();
- }
-
@Nullable
private AutofillManager getAutofillManager() {
return mContext.getSystemService(AutofillManager.class);
@@ -9346,6 +9399,62 @@
}
/**
+ * Dispatches the initial Content Capture events for a view structure.
+ *
+ * @hide
+ */
+ public void dispatchInitialProvideContentCaptureStructure(@NonNull ContentCaptureManager ccm) {
+ AttachInfo ai = mAttachInfo;
+ if (ai == null) {
+ Log.w(CONTENT_CAPTURE_LOG_TAG,
+ "dispatchProvideContentCaptureStructure(): no AttachInfo for " + this);
+ return;
+ }
+
+ // We must set it before checkign if the view itself is important, because it might
+ // initially not be (for example, if it's empty), although that might change later (for
+ // example, if important views are added)
+ ai.mReadyForContentCaptureUpdates = true;
+
+ if (!isImportantForContentCapture()) {
+ if (Log.isLoggable(CONTENT_CAPTURE_LOG_TAG, Log.DEBUG)) {
+ Log.d(CONTENT_CAPTURE_LOG_TAG,
+ "dispatchProvideContentCaptureStructure(): decorView is not important");
+ }
+ return;
+ }
+
+ ai.mContentCaptureManager = ccm;
+
+ ContentCaptureSession session = getContentCaptureSession();
+ if (session == null) {
+ if (Log.isLoggable(CONTENT_CAPTURE_LOG_TAG, Log.DEBUG)) {
+ Log.d(CONTENT_CAPTURE_LOG_TAG,
+ "dispatchProvideContentCaptureStructure(): no session for " + this);
+ }
+ return;
+ }
+
+ session.internalNotifyViewHierarchyEvent(/* started= */ true);
+ try {
+ dispatchProvideContentCaptureStructure();
+ } finally {
+ session.internalNotifyViewHierarchyEvent(/* started= */ false);
+ }
+ }
+
+ /** @hide */
+ void dispatchProvideContentCaptureStructure() {
+ ContentCaptureSession session = getContentCaptureSession();
+ if (session != null) {
+ ViewStructure structure = session.newViewStructure(this);
+ onProvideContentCaptureStructure(structure, /* flags= */ 0);
+ setNotifiedContentCaptureAppeared();
+ session.notifyViewAppeared(structure);
+ }
+ }
+
+ /**
* @see #onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo)
*
* Note: Called from the default {@link AccessibilityDelegate}.
@@ -17457,6 +17566,10 @@
return;
}
+ // Reset content capture caches
+ mPrivateFlags4 &= ~PFLAG4_CONTENT_CAPTURE_IMPORTANCE_MASK;
+ mCachedContentCaptureSession = null;
+
if ((mPrivateFlags & (PFLAG_DRAWN | PFLAG_HAS_BOUNDS)) == (PFLAG_DRAWN | PFLAG_HAS_BOUNDS)
|| (invalidateCache && (mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == PFLAG_DRAWING_CACHE_VALID)
|| (mPrivateFlags & PFLAG_INVALIDATED) != PFLAG_INVALIDATED
@@ -27963,6 +28076,23 @@
View mTooltipHost;
/**
+ * The initial structure has been reported so the view is ready to report updates.
+ */
+ boolean mReadyForContentCaptureUpdates;
+
+ /**
+ * Map of ids (per session) that need to be notified after as gone the view hierchy is
+ * traversed.
+ */
+ // TODO(b/121197119): use SparseArray once session id becomes integer
+ ArrayMap<String, ArrayList<AutofillId>> mContentCaptureRemovedIds;
+
+ /**
+ * Cached reference to the {@link ContentCaptureManager}.
+ */
+ ContentCaptureManager mContentCaptureManager;
+
+ /**
* Creates a new set of attachment information with the specified
* events handler and thread.
*
@@ -27980,6 +28110,31 @@
mRootCallbacks = effectPlayer;
mTreeObserver = new ViewTreeObserver(context);
}
+
+ private void delayNotifyContentCaptureDisappeared(@NonNull ContentCaptureSession session,
+ @NonNull AutofillId id) {
+ if (mContentCaptureRemovedIds == null) {
+ // Most of the time there will be just one session, so intial capacity is 1
+ mContentCaptureRemovedIds = new ArrayMap<>(1);
+ }
+ String sessionId = session.getId();
+ // TODO: life would be much easier if we provided a MultiMap implementation somwhere...
+ ArrayList<AutofillId> ids = mContentCaptureRemovedIds.get(sessionId);
+ if (ids == null) {
+ ids = new ArrayList<>();
+ mContentCaptureRemovedIds.put(sessionId, ids);
+ }
+ ids.add(id);
+ }
+
+ @Nullable
+ private ContentCaptureManager getContentCaptureManager(@NonNull Context context) {
+ if (mContentCaptureManager != null) {
+ return mContentCaptureManager;
+ }
+ mContentCaptureManager = context.getSystemService(ContentCaptureManager.class);
+ return mContentCaptureManager;
+ }
}
/**
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 0986cfa..e0e7843 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -3603,7 +3603,7 @@
return;
}
- final ChildListForAutoFill children = getChildrenForAutofill(flags);
+ final ChildListForAutoFillOrContentCapture children = getChildrenForAutofill(flags);
final int childrenCount = children.size();
structure.setChildCount(childrenCount);
for (int i = 0; i < childrenCount; i++) {
@@ -3614,13 +3614,31 @@
children.recycle();
}
+ /** @hide */
+ @Override
+ public void dispatchProvideContentCaptureStructure() {
+ super.dispatchProvideContentCaptureStructure();
+
+ if (!isLaidOut()) return;
+
+ final ChildListForAutoFillOrContentCapture children = getChildrenForContentCapture();
+ final int childrenCount = children.size();
+ for (int i = 0; i < childrenCount; i++) {
+ final View child = children.get(i);
+ child.dispatchProvideContentCaptureStructure();
+ }
+ children.recycle();
+ }
+
/**
* Gets the children for autofill. Children for autofill are the first
* level descendants that are important for autofill. The returned
* child list object is pooled and the caller must recycle it once done.
* @hide */
- private @NonNull ChildListForAutoFill getChildrenForAutofill(@AutofillFlags int flags) {
- final ChildListForAutoFill children = ChildListForAutoFill.obtain();
+ private @NonNull ChildListForAutoFillOrContentCapture getChildrenForAutofill(
+ @AutofillFlags int flags) {
+ final ChildListForAutoFillOrContentCapture children = ChildListForAutoFillOrContentCapture
+ .obtain();
populateChildrenForAutofill(children, flags);
return children;
}
@@ -3647,6 +3665,34 @@
}
}
+ private @NonNull ChildListForAutoFillOrContentCapture getChildrenForContentCapture() {
+ final ChildListForAutoFillOrContentCapture children = ChildListForAutoFillOrContentCapture
+ .obtain();
+ populateChildrenForContentCapture(children);
+ return children;
+ }
+
+ /** @hide */
+ private void populateChildrenForContentCapture(ArrayList<View> list) {
+ final int childrenCount = mChildrenCount;
+ if (childrenCount <= 0) {
+ return;
+ }
+ final ArrayList<View> preorderedList = buildOrderedChildList();
+ final boolean customOrder = preorderedList == null
+ && isChildrenDrawingOrderEnabled();
+ for (int i = 0; i < childrenCount; i++) {
+ final int childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder);
+ final View child = (preorderedList == null)
+ ? mChildren[childIndex] : preorderedList.get(childIndex);
+ if (child.isImportantForContentCapture()) {
+ list.add(child);
+ } else if (child instanceof ViewGroup) {
+ ((ViewGroup) child).populateChildrenForContentCapture(list);
+ }
+ }
+ }
+
private static View getAndVerifyPreorderedView(ArrayList<View> preorderedList, View[] children,
int childIndex) {
final View child;
@@ -8544,16 +8590,16 @@
/**
* Pooled class that to hold the children for autifill.
*/
- static class ChildListForAutoFill extends ArrayList<View> {
+ private static class ChildListForAutoFillOrContentCapture extends ArrayList<View> {
private static final int MAX_POOL_SIZE = 32;
- private static final Pools.SimplePool<ChildListForAutoFill> sPool =
+ private static final Pools.SimplePool<ChildListForAutoFillOrContentCapture> sPool =
new Pools.SimplePool<>(MAX_POOL_SIZE);
- public static ChildListForAutoFill obtain() {
- ChildListForAutoFill list = sPool.acquire();
+ public static ChildListForAutoFillOrContentCapture obtain() {
+ ChildListForAutoFillOrContentCapture list = sPool.acquire();
if (list == null) {
- list = new ChildListForAutoFill();
+ list = new ChildListForAutoFillOrContentCapture();
}
return list;
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 61fb38f..47528a0 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -103,7 +103,10 @@
import android.view.accessibility.IAccessibilityInteractionConnectionCallback;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.Interpolator;
+import android.view.autofill.AutofillId;
import android.view.autofill.AutofillManager;
+import android.view.contentcapture.ContentCaptureManager;
+import android.view.contentcapture.MainContentCaptureSession;
import android.view.inputmethod.InputMethodManager;
import android.widget.Scroller;
@@ -154,6 +157,7 @@
private static final boolean DEBUG_FPS = false;
private static final boolean DEBUG_INPUT_STAGES = false || LOCAL_LOGV;
private static final boolean DEBUG_KEEP_SCREEN_ON = false || LOCAL_LOGV;
+ private static final boolean DEBUG_CONTENT_CAPTURE = false || LOCAL_LOGV;
/**
* Set to false if we do not want to use the multi threaded renderer even though
@@ -412,6 +416,7 @@
boolean mApplyInsetsRequested;
boolean mLayoutRequested;
boolean mFirst;
+ boolean mPerformContentCapture;
boolean mReportNextDraw;
boolean mFullRedrawNeeded;
boolean mNewSurfaceNeeded;
@@ -608,6 +613,7 @@
mTransparentRegion = new Region();
mPreviousTransparentRegion = new Region();
mFirst = true; // true for the first time the view is added
+ mPerformContentCapture = true; // also true for the first time the view is added
mAdded = false;
mAttachInfo = new View.AttachInfo(mWindowSession, mWindow, display, this, mHandler, this,
context);
@@ -2756,6 +2762,24 @@
}
}
+ if (mAttachInfo.mContentCaptureRemovedIds != null) {
+ MainContentCaptureSession mainSession = mAttachInfo.mContentCaptureManager
+ .getMainContentCaptureSession();
+ Trace.traceBegin(Trace.TRACE_TAG_VIEW, "notifyContentCaptureViewsGone");
+ try {
+ for (int i = 0; i < mAttachInfo.mContentCaptureRemovedIds.size(); i++) {
+ String sessionId = mAttachInfo.mContentCaptureRemovedIds
+ .keyAt(i);
+ ArrayList<AutofillId> ids = mAttachInfo.mContentCaptureRemovedIds
+ .valueAt(i);
+ mainSession.notifyViewsDisappeared(sessionId, ids);
+ }
+ mAttachInfo.mContentCaptureRemovedIds = null;
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_VIEW);
+ }
+ }
+
mIsInTraversal = false;
}
@@ -3451,6 +3475,35 @@
pendingDrawFinished();
}
}
+ if (mPerformContentCapture) {
+ performContentCapture();
+ }
+ }
+
+ private void performContentCapture() {
+ mPerformContentCapture = false; // One-time offer!
+ final View rootView = mView;
+ if (DEBUG_CONTENT_CAPTURE) {
+ Log.v(mTag, "dispatchContentCapture() on " + rootView);
+ }
+ if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
+ Trace.traceBegin(Trace.TRACE_TAG_VIEW, "dispatchContentCapture() for "
+ + getClass().getSimpleName());
+ }
+ try {
+ // First check if context supports it, so it saves a service lookup when it doesn't
+ if (!mContext.isContentCaptureSupported()) return;
+
+ // Then check if it's enabled in the contex itself.
+ final ContentCaptureManager ccm = mContext
+ .getSystemService(ContentCaptureManager.class);
+ if (ccm == null || !ccm.isContentCaptureEnabled()) return;
+
+ // Content capture is a go!
+ rootView.dispatchInitialProvideContentCaptureStructure(ccm);
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_VIEW);
+ }
}
private boolean draw(boolean fullRedrawNeeded) {
diff --git a/core/java/android/view/contentcapture/ChildContentCaptureSession.java b/core/java/android/view/contentcapture/ChildContentCaptureSession.java
index 63c21f3..acb81e0 100644
--- a/core/java/android/view/contentcapture/ChildContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/ChildContentCaptureSession.java
@@ -93,6 +93,11 @@
}
@Override
+ public void internalNotifyViewHierarchyEvent(boolean started) {
+ getMainCaptureSession().notifyInitialViewHierarchyEvent(mId, started);
+ }
+
+ @Override
boolean isContentCaptureEnabled() {
return getMainCaptureSession().isContentCaptureEnabled();
}
diff --git a/core/java/android/view/contentcapture/ContentCaptureEvent.java b/core/java/android/view/contentcapture/ContentCaptureEvent.java
index 205ad30..dfac35d 100644
--- a/core/java/android/view/contentcapture/ContentCaptureEvent.java
+++ b/core/java/android/view/contentcapture/ContentCaptureEvent.java
@@ -71,13 +71,33 @@
*/
public static final int TYPE_VIEW_TEXT_CHANGED = 3;
- // TODO(b/111276913): add event to indicate when FLAG_SECURE was changed?
+ /**
+ * Called before events (such as {@link #TYPE_VIEW_APPEARED}) representing the initial view
+ * hierarchy are sent.
+ *
+ * <p><b>NOTE</b>: there is no guarantee this event will be sent. For example, it's not sent
+ * if the initial view hierarchy doesn't initially have any view that's important for content
+ * capture.
+ */
+ public static final int TYPE_INITIAL_VIEW_TREE_APPEARING = 4;
+
+ /**
+ * Called after events (such as {@link #TYPE_VIEW_APPEARED}) representing the initial view
+ * hierarchy are sent.
+ *
+ * <p><b>NOTE</b>: there is no guarantee this event will be sent. For example, it's not sent
+ * if the initial view hierarchy doesn't initially have any view that's important for content
+ * capture.
+ */
+ public static final int TYPE_INITIAL_VIEW_TREE_APPEARED = 5;
/** @hide */
@IntDef(prefix = { "TYPE_" }, value = {
TYPE_VIEW_APPEARED,
TYPE_VIEW_DISAPPEARED,
- TYPE_VIEW_TEXT_CHANGED
+ TYPE_VIEW_TEXT_CHANGED,
+ TYPE_INITIAL_VIEW_TREE_APPEARING,
+ TYPE_INITIAL_VIEW_TREE_APPEARED
})
@Retention(RetentionPolicy.SOURCE)
public @interface EventType{}
@@ -110,8 +130,10 @@
return this;
}
- private void setAutofillIds(@NonNull ArrayList<AutofillId> ids) {
+ /** @hide */
+ public ContentCaptureEvent setAutofillIds(@NonNull ArrayList<AutofillId> ids) {
mIds = Preconditions.checkNotNull(ids);
+ return this;
}
/**
@@ -195,7 +217,8 @@
* Gets the type of the event.
*
* @return one of {@link #TYPE_VIEW_APPEARED}, {@link #TYPE_VIEW_DISAPPEARED},
- * or {@link #TYPE_VIEW_TEXT_CHANGED}.
+ * {@link #TYPE_VIEW_TEXT_CHANGED}, {@link #TYPE_INITIAL_VIEW_TREE_APPEARING}, or
+ * {@link #TYPE_INITIAL_VIEW_TREE_APPEARED}.
*/
public @EventType int getType() {
return mType;
@@ -374,6 +397,10 @@
return "VIEW_DISAPPEARED";
case TYPE_VIEW_TEXT_CHANGED:
return "VIEW_TEXT_CHANGED";
+ case TYPE_INITIAL_VIEW_TREE_APPEARING:
+ return "INITIAL_VIEW_HIERARCHY_STARTED";
+ case TYPE_INITIAL_VIEW_TREE_APPEARED:
+ return "INITIAL_VIEW_HIERARCHY_FINISHED";
default:
return "UKNOWN_TYPE: " + type;
}
diff --git a/core/java/android/view/contentcapture/ContentCaptureSession.java b/core/java/android/view/contentcapture/ContentCaptureSession.java
index 68a3e8a..e028961 100644
--- a/core/java/android/view/contentcapture/ContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/ContentCaptureSession.java
@@ -204,6 +204,12 @@
return mId.hashCode();
}
+ /** @hide */
+ @NonNull
+ public String getId() {
+ return mId;
+ }
+
/**
* Creates a new {@link ContentCaptureSession}.
*
@@ -362,6 +368,9 @@
abstract void internalNotifyViewTextChanged(@NonNull AutofillId id,
@Nullable CharSequence text);
+ /** @hide */
+ public abstract void internalNotifyViewHierarchyEvent(boolean started);
+
/**
* Creates a {@link ViewStructure} for a "standard" view.
*
diff --git a/core/java/android/view/contentcapture/MainContentCaptureSession.java b/core/java/android/view/contentcapture/MainContentCaptureSession.java
index 034c8fa..0605fa3 100644
--- a/core/java/android/view/contentcapture/MainContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/MainContentCaptureSession.java
@@ -15,6 +15,8 @@
*/
package android.view.contentcapture;
+import static android.view.contentcapture.ContentCaptureEvent.TYPE_INITIAL_VIEW_TREE_APPEARED;
+import static android.view.contentcapture.ContentCaptureEvent.TYPE_INITIAL_VIEW_TREE_APPEARING;
import static android.view.contentcapture.ContentCaptureEvent.TYPE_SESSION_FINISHED;
import static android.view.contentcapture.ContentCaptureEvent.TYPE_SESSION_STARTED;
import static android.view.contentcapture.ContentCaptureEvent.TYPE_VIEW_APPEARED;
@@ -66,6 +68,8 @@
private static final String TAG = MainContentCaptureSession.class.getSimpleName();
+ private static final boolean FORCE_FLUSH = true;
+
/**
* Handler message used to flush the buffer.
*/
@@ -270,6 +274,10 @@
}
}
+ private void handleSendEvent(@NonNull ContentCaptureEvent event) {
+ handleSendEvent(event, /* forceFlush= */ false);
+ }
+
private void handleSendEvent(@NonNull ContentCaptureEvent event, boolean forceFlush) {
final int eventType = event.getType();
if (VERBOSE) Log.v(TAG, "handleSendEvent(" + getDebugState() + "): " + event);
@@ -518,6 +526,11 @@
}
@Override
+ public void internalNotifyViewHierarchyEvent(boolean started) {
+ notifyInitialViewHierarchyEvent(mId, started);
+ }
+
+ @Override
boolean isContentCaptureEnabled() {
return super.isContentCaptureEnabled() && mManager.isContentCaptureEnabled();
}
@@ -536,33 +549,57 @@
new ContentCaptureEvent(childSessionId, TYPE_SESSION_STARTED)
.setParentSessionId(parentSessionId)
.setClientContext(clientContext),
- /* forceFlush= */ true));
+ FORCE_FLUSH));
}
void notifyChildSessionFinished(@NonNull String parentSessionId,
@NonNull String childSessionId) {
mHandler.sendMessage(obtainMessage(MainContentCaptureSession::handleSendEvent, this,
new ContentCaptureEvent(childSessionId, TYPE_SESSION_FINISHED)
- .setParentSessionId(parentSessionId), /* forceFlush= */ true));
+ .setParentSessionId(parentSessionId), FORCE_FLUSH));
}
void notifyViewAppeared(@NonNull String sessionId, @NonNull ViewStructureImpl node) {
mHandler.sendMessage(obtainMessage(MainContentCaptureSession::handleSendEvent, this,
- new ContentCaptureEvent(sessionId, TYPE_VIEW_APPEARED)
- .setViewNode(node.mNode), /* forceFlush= */ false));
+ new ContentCaptureEvent(sessionId, TYPE_VIEW_APPEARED).setViewNode(node.mNode)));
}
void notifyViewDisappeared(@NonNull String sessionId, @NonNull AutofillId id) {
mHandler.sendMessage(obtainMessage(MainContentCaptureSession::handleSendEvent, this,
- new ContentCaptureEvent(sessionId, TYPE_VIEW_DISAPPEARED).setAutofillId(id),
- /* forceFlush= */ false));
+ new ContentCaptureEvent(sessionId, TYPE_VIEW_DISAPPEARED).setAutofillId(id)));
+ }
+
+ /** @hide */
+ public void notifyViewsDisappeared(@NonNull String sessionId,
+ @NonNull ArrayList<AutofillId> ids) {
+ final ContentCaptureEvent event = new ContentCaptureEvent(sessionId, TYPE_VIEW_DISAPPEARED);
+ if (ids.size() == 1) {
+ event.setAutofillId(ids.get(0));
+ } else {
+ event.setAutofillIds(ids);
+ }
+
+ mHandler.sendMessage(
+ obtainMessage(MainContentCaptureSession::handleSendEvent, this, event));
}
void notifyViewTextChanged(@NonNull String sessionId, @NonNull AutofillId id,
@Nullable CharSequence text) {
mHandler.sendMessage(obtainMessage(MainContentCaptureSession::handleSendEvent, this,
new ContentCaptureEvent(sessionId, TYPE_VIEW_TEXT_CHANGED).setAutofillId(id)
- .setText(text), /* forceFlush= */ false));
+ .setText(text)));
+ }
+
+ void notifyInitialViewHierarchyEvent(@NonNull String sessionId, boolean started) {
+ if (started) {
+ mHandler.sendMessage(obtainMessage(MainContentCaptureSession::handleSendEvent, this,
+ new ContentCaptureEvent(sessionId, TYPE_INITIAL_VIEW_TREE_APPEARING)));
+ } else {
+ mHandler.sendMessage(obtainMessage(MainContentCaptureSession::handleSendEvent, this,
+ new ContentCaptureEvent(sessionId, TYPE_INITIAL_VIEW_TREE_APPEARED),
+ FORCE_FLUSH));
+
+ }
}
@Override
diff --git a/core/java/com/android/internal/policy/DecorContext.java b/core/java/com/android/internal/policy/DecorContext.java
index cd80d53..429c618 100644
--- a/core/java/com/android/internal/policy/DecorContext.java
+++ b/core/java/com/android/internal/policy/DecorContext.java
@@ -22,6 +22,7 @@
import android.view.ContextThemeWrapper;
import android.view.WindowManager;
import android.view.WindowManagerImpl;
+import android.view.contentcapture.ContentCaptureManager;
import java.lang.ref.WeakReference;
@@ -36,6 +37,7 @@
private PhoneWindow mPhoneWindow;
private WindowManager mWindowManager;
private Resources mActivityResources;
+ private ContentCaptureManager mContentCaptureManager;
private WeakReference<Context> mActivityContext;
@@ -60,6 +62,16 @@
}
return mWindowManager;
}
+ if (Context.CONTENT_CAPTURE_MANAGER_SERVICE.equals(name)) {
+ if (mContentCaptureManager == null) {
+ Context activityContext = mActivityContext.get();
+ if (activityContext != null) {
+ mContentCaptureManager = (ContentCaptureManager) activityContext
+ .getSystemService(name);
+ }
+ }
+ return mContentCaptureManager;
+ }
return super.getSystemService(name);
}
@@ -79,4 +91,9 @@
public AssetManager getAssets() {
return mActivityResources.getAssets();
}
+
+ @Override
+ public boolean isContentCaptureSupported() {
+ return true;
+ }
}
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 49598bb..d69d416 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -230,6 +230,11 @@
extern int register_com_android_internal_os_ZygoteInit(JNIEnv *env);
extern int register_com_android_internal_util_VirtualRefBasePtr(JNIEnv *env);
+// Namespace for Android Runtime flags applied during boot time.
+static const char* RUNTIME_NATIVE_BOOT_NAMESPACE = "runtime_native_boot";
+// Feature flag name for Garbage Collector type.
+static const char* GCTYPE = "gctype";
+
static AndroidRuntime* gCurRuntime = NULL;
/*
@@ -776,7 +781,9 @@
}
std::string gc_type_override =
- server_configurable_flags::GetServerConfigurableFlag("runtime_native", "gctype", "");
+ server_configurable_flags::GetServerConfigurableFlag(RUNTIME_NATIVE_BOOT_NAMESPACE,
+ GCTYPE,
+ /*default_value=*/ "");
std::string gc_type_override_temp;
if (gc_type_override.empty()) {
parseRuntimeOption("dalvik.vm.gctype", gctypeOptsBuf, "-Xgc:");
diff --git a/core/tests/coretests/Android.mk b/core/tests/coretests/Android.mk
index 6a81050..40ebd44 100644
--- a/core/tests/coretests/Android.mk
+++ b/core/tests/coretests/Android.mk
@@ -36,12 +36,11 @@
frameworks-core-util-lib \
mockwebserver \
guava \
- androidx.test.runner \
- androidx.test.ext.junit \
- androidx.test.rules \
androidx.test.espresso.core \
+ androidx.test.ext.junit \
+ androidx.test.runner \
+ androidx.test.rules \
mockito-target-minus-junit4 \
- espresso-core \
ub-uiautomator \
platform-test-annotations \
truth-prebuilt \
diff --git a/core/tests/coretests/src/android/animation/AnimatorInflaterTest.java b/core/tests/coretests/src/android/animation/AnimatorInflaterTest.java
index be1d44c..c71bc73 100644
--- a/core/tests/coretests/src/android/animation/AnimatorInflaterTest.java
+++ b/core/tests/coretests/src/android/animation/AnimatorInflaterTest.java
@@ -16,22 +16,28 @@
package android.animation;
-import android.test.ActivityInstrumentationTestCase2;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
import androidx.test.filters.LargeTest;
+import androidx.test.rule.ActivityTestRule;
import com.android.frameworks.coretests.R;
+import org.junit.Rule;
+import org.junit.Test;
+
import java.util.HashSet;
import java.util.Set;
@LargeTest
-public class AnimatorInflaterTest extends ActivityInstrumentationTestCase2<BasicAnimatorActivity> {
- Set<Integer> identityHashes = new HashSet<Integer>();
+public class AnimatorInflaterTest {
+ @Rule
+ public final ActivityTestRule<BasicAnimatorActivity> mActivityRule =
+ new ActivityTestRule<>(BasicAnimatorActivity.class);
- public AnimatorInflaterTest() {
- super(BasicAnimatorActivity.class);
- }
+ private final Set<Integer> mIdentityHashes = new HashSet<>();
private void assertUnique(Object object) {
assertUnique(object, "");
@@ -39,15 +45,16 @@
private void assertUnique(Object object, String msg) {
final int code = System.identityHashCode(object);
- assertTrue("object should be unique " + msg + ", obj:" + object, identityHashes.add(code));
-
+ assertTrue("object should be unique " + msg + ", obj:" + object, mIdentityHashes.add(code));
}
+ @Test
public void testLoadStateListAnimator() {
- StateListAnimator sla1 = AnimatorInflater.loadStateListAnimator(getActivity(),
+ final BasicAnimatorActivity activity = mActivityRule.getActivity();
+ StateListAnimator sla1 = AnimatorInflater.loadStateListAnimator(activity,
R.anim.test_state_anim);
- sla1.setTarget(getActivity().mAnimatingButton);
- StateListAnimator sla2 = AnimatorInflater.loadStateListAnimator(getActivity(),
+ sla1.setTarget(activity.mAnimatingButton);
+ StateListAnimator sla2 = AnimatorInflater.loadStateListAnimator(activity,
R.anim.test_state_anim);
assertNull(sla2.getTarget());
for (StateListAnimator sla : new StateListAnimator[]{sla1, sla2}) {
diff --git a/core/tests/coretests/src/android/animation/AnimatorSetActivityTest.java b/core/tests/coretests/src/android/animation/AnimatorSetActivityTest.java
index 55837ba..7a1de0c 100644
--- a/core/tests/coretests/src/android/animation/AnimatorSetActivityTest.java
+++ b/core/tests/coretests/src/android/animation/AnimatorSetActivityTest.java
@@ -16,27 +16,38 @@
package android.animation;
-import android.test.ActivityInstrumentationTestCase2;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
import android.view.View;
import androidx.test.annotation.UiThreadTest;
import androidx.test.filters.SmallTest;
+import androidx.test.rule.ActivityTestRule;
import com.android.frameworks.coretests.R;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+
import java.util.ArrayList;
-public class AnimatorSetActivityTest extends ActivityInstrumentationTestCase2<AnimatorSetActivity> {
+@SmallTest
+public class AnimatorSetActivityTest {
+
+ @Rule
+ public final ActivityTestRule<AnimatorSetActivity> mActivityRule =
+ new ActivityTestRule<>(AnimatorSetActivity.class);
private static final long POLL_INTERVAL = 100; // ms
private AnimatorSetActivity mActivity;
private ObjectAnimator a1,a2,a3;
private ValueAnimator a4,a5;
- public AnimatorSetActivityTest() {
- super(AnimatorSetActivity.class);
- }
-
static class MyListener implements Animator.AnimatorListener {
boolean startIsCalled = false;
boolean endIsCalled = false;
@@ -63,10 +74,9 @@
}
}
- @Override
+ @Before
public void setUp() throws Exception {
- super.setUp();
- mActivity = getActivity();
+ mActivity = mActivityRule.getActivity();
View square1 = mActivity.findViewById(R.id.square1);
View square2 = mActivity.findViewById(R.id.square2);
@@ -78,7 +88,7 @@
a5 = ValueAnimator.ofFloat(10f, 5f).setDuration(850);
}
- @Override
+ @After
public void tearDown() throws Exception {
mActivity = null;
a1 = null;
@@ -86,10 +96,9 @@
a3 = null;
a4 = null;
a5 = null;
- super.tearDown();
}
- @SmallTest
+ @Test
public void testGetChildAnimations() {
AnimatorSet s1 = new AnimatorSet();
s1.playTogether(a1, a2, a3);
@@ -129,7 +138,7 @@
}
}
- @SmallTest
+ @Test
public void testTotalDuration() {
ArrayList<Animator> list = getAnimatorList();
@@ -192,7 +201,7 @@
}
- @SmallTest
+ @Test
public void testGetDuration() {
AnimatorSet s = new AnimatorSet();
assertTrue(s.getDuration() < 0);
@@ -205,8 +214,8 @@
}
- @SmallTest
@UiThreadTest
+ @Test
public void testSetDuration() {
AnimatorSet s = getSequentialSet();
assertTrue(s.getDuration() < 0);
@@ -224,7 +233,7 @@
assertEquals(duration, a5.getDuration());
}
- @SmallTest
+ @Test
public void testAddListener() throws InterruptedException {
// Verify that the listener is added to the list of listeners in the AnimatorSet
// and that newly added listener gets callback for lifecycle events of the animator
@@ -241,13 +250,10 @@
assertFalse(listener.endIsCalled);
try {
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- s.start();
- assertTrue(listener.startIsCalled);
- assertFalse(listener.endIsCalled);
- }
+ mActivityRule.runOnUiThread(() -> {
+ s.start();
+ assertTrue(listener.startIsCalled);
+ assertFalse(listener.endIsCalled);
});
} catch (Throwable throwable) {
throwable.printStackTrace();
@@ -258,18 +264,13 @@
assertTrue(listener.endIsCalled);
}
- @SmallTest
+ @Test
public void testRemoveListener() throws Throwable {
final AnimatorSet s = new AnimatorSet();
s.playTogether(a1, a2, a3, a4);
MyListener listener = new MyListener();
s.addListener(listener);
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- s.start();
- }
- });
+ mActivityRule.runOnUiThread(s::start);
Thread.sleep(s.getTotalDuration() + 100);
assertTrue(listener.startIsCalled);
@@ -282,18 +283,13 @@
listener.startIsCalled = false;
listener.endIsCalled = false;
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- s.start();
- }
- });
+ mActivityRule.runOnUiThread(s::start);
Thread.sleep(s.getTotalDuration() + 100);
assertFalse(listener.startIsCalled);
assertFalse(listener.endIsCalled);
}
- @SmallTest
+ @Test
public void testEnd() throws Throwable {
// End animator set
final AnimatorSet s = new AnimatorSet();
@@ -301,37 +297,30 @@
final MyListener listener = new MyListener();
s.addListener(listener);
assertFalse(listener.endIsCalled);
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- s.start();
- assertTrue(s.isStarted());
- assertTrue(listener.startIsCalled);
- assertFalse(listener.endIsCalled);
- }
+ mActivityRule.runOnUiThread(() -> {
+ s.start();
+ assertTrue(s.isStarted());
+ assertTrue(listener.startIsCalled);
+ assertFalse(listener.endIsCalled);
});
Thread.sleep(a2.getTotalDuration());
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- s.end();
- assertTrue(listener.startIsCalled);
- assertTrue(listener.endIsCalled);
- assertFalse(s.isRunning());
- assertFalse(s.isStarted());
+ mActivityRule.runOnUiThread(() -> {
+ s.end();
+ assertTrue(listener.startIsCalled);
+ assertTrue(listener.endIsCalled);
+ assertFalse(s.isRunning());
+ assertFalse(s.isStarted());
- assertFalse(a1.isStarted());
- assertFalse(a2.isStarted());
- assertFalse(a3.isStarted());
- assertFalse(a4.isStarted());
- }
+ assertFalse(a1.isStarted());
+ assertFalse(a2.isStarted());
+ assertFalse(a3.isStarted());
+ assertFalse(a4.isStarted());
});
-
}
- @SmallTest
+ @Test
public void testStart() throws Throwable {
final AnimatorSet s = new AnimatorSet();
ArrayList<Animator> animators = getAnimatorList();
@@ -355,12 +344,9 @@
assertFalse(l.endIsCalled);
}
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- s.start();
- assertTrue(l.startIsCalled);
- }
+ mActivityRule.runOnUiThread(() -> {
+ s.start();
+ assertTrue(l.startIsCalled);
});
long timeout = s.getTotalDuration() * 2;
@@ -383,7 +369,7 @@
}
}
- @SmallTest
+ @Test
public void testCancel() throws Throwable {
// Check whether cancel would trigger onAnimationCanceled and cancel all the unfinished
// animations
@@ -411,42 +397,33 @@
assertFalse(l.endIsCalled);
}
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- s.start();
- }
- });
+ mActivityRule.runOnUiThread(s::start);
Thread.sleep(a1.getTotalDuration());
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- assertTrue(s.isStarted());
- ArrayList<Integer> runningAnimIds = new ArrayList<Integer>();
- for (int i = 0; i < animators.size(); i++) {
- if (animators.get(i).isStarted()) {
- runningAnimIds.add(i);
- }
- }
- s.cancel();
- assertTrue(l.startIsCalled);
- assertTrue(l.cancelIsCalled);
- assertTrue(l.endIsCalled);
-
- for (int i = 0; i < listeners.size(); i++) {
- assertTrue(listeners.get(i).startIsCalled);
- if (runningAnimIds.contains(i)) {
- assertTrue(listeners.get(i).cancelIsCalled);
- }
- assertTrue(listeners.get(i).endIsCalled);
+ mActivityRule.runOnUiThread(() -> {
+ assertTrue(s.isStarted());
+ ArrayList<Integer> runningAnimIds = new ArrayList<>();
+ for (int i = 0; i < animators.size(); i++) {
+ if (animators.get(i).isStarted()) {
+ runningAnimIds.add(i);
}
}
- });
+ s.cancel();
+ assertTrue(l.startIsCalled);
+ assertTrue(l.cancelIsCalled);
+ assertTrue(l.endIsCalled);
+ for (int i = 0; i < listeners.size(); i++) {
+ assertTrue(listeners.get(i).startIsCalled);
+ if (runningAnimIds.contains(i)) {
+ assertTrue(listeners.get(i).cancelIsCalled);
+ }
+ assertTrue(listeners.get(i).endIsCalled);
+ }
+ });
}
- @SmallTest
+ @Test
public void testIsRunning() throws Throwable {
final AnimatorSet s = new AnimatorSet();
final long startDelay = 500;
@@ -455,12 +432,7 @@
s.setStartDelay(startDelay);
MyListener listener = new MyListener();
s.addListener(listener);
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- s.start();
- }
- });
+ mActivityRule.runOnUiThread(s::start);
while (!listener.endIsCalled) {
boolean passedStartDelay = a1.isStarted() || a2.isStarted() || a3.isStarted() ||
@@ -471,35 +443,29 @@
assertFalse(s.isRunning());
}
- @SmallTest
+ @Test
public void testPauseAndResume() throws Throwable {
final AnimatorSet set = getSequentialSet();
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- // Calling pause before start should have no effect, per documentation
- set.pause();
- set.start();
- assertFalse(set.isPaused());
- }
+ mActivityRule.runOnUiThread(() -> {
+ // Calling pause before start should have no effect, per documentation
+ set.pause();
+ set.start();
+ assertFalse(set.isPaused());
});
while (!a2.isStarted()) {
Thread.sleep(50);
}
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- assertFalse(set.isPaused());
- set.pause();
- assertTrue(set.isPaused());
- set.resume();
- assertFalse(set.isPaused());
- }
+ mActivityRule.runOnUiThread(() -> {
+ assertFalse(set.isPaused());
+ set.pause();
+ assertTrue(set.isPaused());
+ set.resume();
+ assertFalse(set.isPaused());
});
}
- @SmallTest
+ @Test
public void testClone() throws Throwable {
// Set up an AnimatorSet and two clones, add one listener to each. When the clones animate,
// listeners of both the clone and the animator being cloned should receive animation
@@ -535,14 +501,11 @@
// Start the animation, and make the first clone during its run and the second clone once
// it ends.
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- assertFalse(l1.startIsCalled);
- assertFalse(l1.endIsCalled);
+ mActivityRule.runOnUiThread(() -> {
+ assertFalse(l1.startIsCalled);
+ assertFalse(l1.endIsCalled);
- s1.start();
- }
+ s1.start();
});
// Make the first clone, during the animation's run.
@@ -552,20 +515,12 @@
s2.addListener(l2);
Thread.sleep(POLL_INTERVAL);
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- s1.end();
- }
- });
+ mActivityRule.runOnUiThread(s1::end);
Thread.sleep(POLL_INTERVAL);
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- assertTrue(l1.startIsCalled);
- assertTrue(l1.endIsCalled);
- }
+ mActivityRule.runOnUiThread(() -> {
+ assertTrue(l1.startIsCalled);
+ assertTrue(l1.endIsCalled);
});
Thread.sleep(POLL_INTERVAL);
@@ -574,59 +529,49 @@
final MyListener l3 = new MyListener();
s3.addListener(l3);
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- // Checking the fields before animations start.
- assertFalse(l2.startIsCalled);
- assertFalse(l2.cancelIsCalled);
- assertFalse(l2.endIsCalled);
- assertFalse(l3.startIsCalled);
- assertFalse(l3.cancelIsCalled);
- assertFalse(l3.endIsCalled);
+ mActivityRule.runOnUiThread(() -> {
+ // Checking the fields before animations start.
+ assertFalse(l2.startIsCalled);
+ assertFalse(l2.cancelIsCalled);
+ assertFalse(l2.endIsCalled);
+ assertFalse(l3.startIsCalled);
+ assertFalse(l3.cancelIsCalled);
+ assertFalse(l3.endIsCalled);
- s2.start();
- s3.start();
- }
+ s2.start();
+ s3.start();
});
Thread.sleep(POLL_INTERVAL);
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- // Make sure the listeners receive the callbacks
- // At this time only onAnimationStart() should be called.
- assertTrue(l2.startIsCalled);
- assertTrue(l3.startIsCalled);
- assertFalse(l2.endIsCalled);
- assertFalse(l3.endIsCalled);
- assertFalse(l2.cancelIsCalled);
- assertFalse(l3.cancelIsCalled);
+ mActivityRule.runOnUiThread(() -> {
+ // Make sure the listeners receive the callbacks
+ // At this time only onAnimationStart() should be called.
+ assertTrue(l2.startIsCalled);
+ assertTrue(l3.startIsCalled);
+ assertFalse(l2.endIsCalled);
+ assertFalse(l3.endIsCalled);
+ assertFalse(l2.cancelIsCalled);
+ assertFalse(l3.cancelIsCalled);
- s2.end();
- s3.cancel();
- }
+ s2.end();
+ s3.cancel();
});
Thread.sleep(POLL_INTERVAL);
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- // Check that the new listeners for the new animations gets called for the events.
- assertTrue(l2.startIsCalled);
- assertFalse(l2.cancelIsCalled);
- assertTrue(l2.endIsCalled);
- assertTrue(l3.startIsCalled);
- assertTrue(l3.cancelIsCalled);
- assertTrue(l3.endIsCalled);
+ mActivityRule.runOnUiThread(() -> {
+ // Check that the new listeners for the new animations gets called for the events.
+ assertTrue(l2.startIsCalled);
+ assertFalse(l2.cancelIsCalled);
+ assertTrue(l2.endIsCalled);
+ assertTrue(l3.startIsCalled);
+ assertTrue(l3.cancelIsCalled);
+ assertTrue(l3.endIsCalled);
- // Check that the listener on the animation that was being clone receive the
- // animation lifecycle events for the clones.
- assertTrue(onlyContains(startedAnimators, s1, s2, s3));
- assertTrue(onlyContains(canceledAnimators, s3));
- assertTrue(onlyContains(endedAnimators, s1, s2, s3));
- }
+ // Check that the listener on the animation that was being clone receive the
+ // animation lifecycle events for the clones.
+ assertTrue(onlyContains(startedAnimators, s1, s2, s3));
+ assertTrue(onlyContains(canceledAnimators, s3));
+ assertTrue(onlyContains(endedAnimators, s1, s2, s3));
});
-
}
/**
@@ -663,5 +608,4 @@
list.add(a5);
return list;
}
-
}
diff --git a/core/tests/coretests/src/android/animation/AnimatorSetEventsTest.java b/core/tests/coretests/src/android/animation/AnimatorSetEventsTest.java
index 4e90d1a..94c90aa 100644
--- a/core/tests/coretests/src/android/animation/AnimatorSetEventsTest.java
+++ b/core/tests/coretests/src/android/animation/AnimatorSetEventsTest.java
@@ -23,6 +23,8 @@
import com.android.frameworks.coretests.R;
+import org.junit.Test;
+
import java.util.concurrent.TimeUnit;
/**
@@ -36,7 +38,7 @@
@Override
public void setUp() throws Exception {
- button = (Button) getActivity().findViewById(R.id.animatingButton);
+ button = mActivityRule.getActivity().findViewById(R.id.animatingButton);
mAnimator = new AnimatorSet();
((AnimatorSet)mAnimator).playSequentially(xAnim, yAnim);
super.setUp();
@@ -53,23 +55,21 @@
* its children
*/
@MediumTest
- public void testPlayingCancelDuringChildDelay() throws Exception {
+ @Test
+ public void testPlayingCancelDuringChildDelay() throws Throwable {
yAnim.setStartDelay(500);
final AnimatorSet animSet = new AnimatorSet();
animSet.playSequentially(xAnim, yAnim);
mFutureListener = new FutureReleaseListener(mFuture);
- getActivity().runOnUiThread(new Runnable() {
- @Override
- public void run() {
- try {
- Handler handler = new Handler();
- animSet.addListener(mFutureListener);
- mRunning = true;
- animSet.start();
- handler.postDelayed(new Canceler(animSet, mFuture), ANIM_DURATION + 250);
- } catch (junit.framework.AssertionFailedError e) {
- mFuture.setException(new RuntimeException(e));
- }
+ mActivityRule.runOnUiThread(() -> {
+ try {
+ Handler handler = new Handler();
+ animSet.addListener(mFutureListener);
+ mRunning = true;
+ animSet.start();
+ handler.postDelayed(new Canceler(animSet, mFuture), ANIM_DURATION + 250);
+ } catch (junit.framework.AssertionFailedError e) {
+ mFuture.setException(new RuntimeException(e));
}
});
mFuture.get(getTimeout(), TimeUnit.MILLISECONDS);
diff --git a/core/tests/coretests/src/android/animation/AutoCancelTest.java b/core/tests/coretests/src/android/animation/AutoCancelTest.java
index b3ec92c..7df7336 100644
--- a/core/tests/coretests/src/android/animation/AutoCancelTest.java
+++ b/core/tests/coretests/src/android/animation/AutoCancelTest.java
@@ -16,15 +16,24 @@
package android.animation;
+import static org.junit.Assert.assertTrue;
+
import android.os.Handler;
-import android.test.ActivityInstrumentationTestCase2;
import androidx.test.filters.SmallTest;
+import androidx.test.rule.ActivityTestRule;
+
+import org.junit.Rule;
+import org.junit.Test;
import java.util.HashMap;
import java.util.concurrent.TimeUnit;
-public class AutoCancelTest extends ActivityInstrumentationTestCase2<BasicAnimatorActivity> {
+public class AutoCancelTest {
+
+ @Rule
+ public final ActivityTestRule<BasicAnimatorActivity> mActivityRule =
+ new ActivityTestRule<>(BasicAnimatorActivity.class);
boolean mAnimX1Canceled = false;
boolean mAnimXY1Canceled = false;
@@ -37,10 +46,6 @@
HashMap<Animator, Boolean> mCanceledMap = new HashMap<Animator, Boolean>();
- public AutoCancelTest() {
- super(BasicAnimatorActivity.class);
- }
-
ObjectAnimator setupAnimator(long startDelay, String... properties) {
ObjectAnimator returnVal;
if (properties.length == 1) {
@@ -58,8 +63,7 @@
return returnVal;
}
- private void setupAnimators(long startDelay, boolean startLater, final FutureWaiter future)
- throws Exception {
+ private void setupAnimators(long startDelay, boolean startLater, final FutureWaiter future) {
// Animators to be auto-canceled
final ObjectAnimator animX1 = setupAnimator(startDelay, "x");
final ObjectAnimator animY1 = setupAnimator(startDelay, "y");
@@ -123,64 +127,56 @@
}
@SmallTest
- public void testAutoCancel() throws Exception {
+ @Test
+ public void testAutoCancel() throws Throwable {
final FutureWaiter future = new FutureWaiter();
- getActivity().runOnUiThread(new Runnable() {
- @Override
- public void run() {
- try {
- setupAnimators(0, false, future);
- } catch (Exception e) {
- future.setException(e);
- }
+ mActivityRule.runOnUiThread(() -> {
+ try {
+ setupAnimators(0, false, future);
+ } catch (Exception e) {
+ future.setException(e);
}
});
assertTrue(future.get(FUTURE_TIMEOUT, TimeUnit.MILLISECONDS));
}
@SmallTest
- public void testAutoCancelDelayed() throws Exception {
+ @Test
+ public void testAutoCancelDelayed() throws Throwable {
final FutureWaiter future = new FutureWaiter();
- getActivity().runOnUiThread(new Runnable() {
- @Override
- public void run() {
- try {
- setupAnimators(START_DELAY, false, future);
- } catch (Exception e) {
- future.setException(e);
- }
+ mActivityRule.runOnUiThread(() -> {
+ try {
+ setupAnimators(START_DELAY, false, future);
+ } catch (Exception e) {
+ future.setException(e);
}
});
assertTrue(future.get(FUTURE_TIMEOUT, TimeUnit.MILLISECONDS));
}
@SmallTest
- public void testAutoCancelTestLater() throws Exception {
+ @Test
+ public void testAutoCancelTestLater() throws Throwable {
final FutureWaiter future = new FutureWaiter();
- getActivity().runOnUiThread(new Runnable() {
- @Override
- public void run() {
- try {
- setupAnimators(0, true, future);
- } catch (Exception e) {
- future.setException(e);
- }
+ mActivityRule.runOnUiThread(() -> {
+ try {
+ setupAnimators(0, true, future);
+ } catch (Exception e) {
+ future.setException(e);
}
});
assertTrue(future.get(FUTURE_TIMEOUT, TimeUnit.MILLISECONDS));
}
@SmallTest
- public void testAutoCancelDelayedTestLater() throws Exception {
+ @Test
+ public void testAutoCancelDelayedTestLater() throws Throwable {
final FutureWaiter future = new FutureWaiter();
- getActivity().runOnUiThread(new Runnable() {
- @Override
- public void run() {
- try {
- setupAnimators(START_DELAY, true, future);
- } catch (Exception e) {
- future.setException(e);
- }
+ mActivityRule.runOnUiThread(() -> {
+ try {
+ setupAnimators(START_DELAY, true, future);
+ } catch (Exception e) {
+ future.setException(e);
}
});
assertTrue(future.get(FUTURE_TIMEOUT, TimeUnit.MILLISECONDS));
diff --git a/core/tests/coretests/src/android/animation/EventsTest.java b/core/tests/coretests/src/android/animation/EventsTest.java
index ba7413a..0c40a95 100644
--- a/core/tests/coretests/src/android/animation/EventsTest.java
+++ b/core/tests/coretests/src/android/animation/EventsTest.java
@@ -16,12 +16,20 @@
package android.animation;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.annotation.CallSuper;
import android.os.Handler;
-import android.test.ActivityInstrumentationTestCase2;
import androidx.test.annotation.UiThreadTest;
import androidx.test.filters.MediumTest;
import androidx.test.filters.SmallTest;
+import androidx.test.rule.ActivityTestRule;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
@@ -38,8 +46,11 @@
* wait for some later event to occur before ending. These tests use a combination of an
* AbstractFuture mechanism and a delayed action to release that Future later.
*/
-public abstract class EventsTest
- extends ActivityInstrumentationTestCase2<BasicAnimatorActivity> {
+public abstract class EventsTest {
+
+ @Rule
+ public final ActivityTestRule<BasicAnimatorActivity> mActivityRule =
+ new ActivityTestRule<>(BasicAnimatorActivity.class);
protected static final int ANIM_DURATION = 400;
protected static final int ANIM_DELAY = 100;
@@ -55,7 +66,6 @@
private boolean mCanceled; // tracks whether we've canceled the animator
protected Animator.AnimatorListener mFutureListener; // mechanism for delaying end of the test
protected FutureWaiter mFuture; // Mechanism for waiting for the UI test to complete
- private Animator.AnimatorListener mListener; // Listener that handles/tests the events
protected Animator mAnimator; // The animator used in the tests. Must be set in subclass
// setup() method prior to calling the superclass setup()
@@ -67,10 +77,12 @@
protected static class Canceler implements Runnable {
Animator mAnim;
FutureWaiter mFuture;
+
public Canceler(Animator anim, FutureWaiter future) {
mAnim = anim;
mFuture = future;
}
+
@Override
public void run() {
try {
@@ -79,7 +91,7 @@
mFuture.setException(new RuntimeException(e));
}
}
- };
+ }
/**
* Timeout length, based on when the animation should reasonably be complete.
@@ -95,10 +107,12 @@
static class Ender implements Runnable {
Animator mAnim;
FutureWaiter mFuture;
+
public Ender(Animator anim, FutureWaiter future) {
mAnim = anim;
mFuture = future;
}
+
@Override
public void run() {
try {
@@ -107,7 +121,7 @@
mFuture.setException(new RuntimeException(e));
}
}
- };
+ }
/**
* Pauses the given animator. Used to delay pausing until some later time (after the
@@ -116,10 +130,12 @@
static class Pauser implements Runnable {
Animator mAnim;
FutureWaiter mFuture;
+
public Pauser(Animator anim, FutureWaiter future) {
mAnim = anim;
mFuture = future;
}
+
@Override
public void run() {
try {
@@ -128,7 +144,7 @@
mFuture.setException(new RuntimeException(e));
}
}
- };
+ }
/**
* Resumes the given animator. Used to delay resuming until some later time (after the
@@ -137,10 +153,12 @@
static class Resumer implements Runnable {
Animator mAnim;
FutureWaiter mFuture;
+
public Resumer(Animator anim, FutureWaiter future) {
mAnim = anim;
mFuture = future;
}
+
@Override
public void run() {
try {
@@ -149,7 +167,7 @@
mFuture.setException(new RuntimeException(e));
}
}
- };
+ }
/**
* Releases the given Future object when the listener's end() event is called. Specifically,
@@ -171,28 +189,14 @@
public FutureReleaseListener(FutureWaiter future, long timeout) {
mFuture = future;
Handler handler = new Handler();
- handler.postDelayed(new Runnable() {
- @Override
- public void run() {
- mFuture.release();
- }
- }, timeout);
+ handler.postDelayed(mFuture::release, timeout);
}
@Override
public void onAnimationEnd(Animator animation) {
Handler handler = new Handler();
- handler.postDelayed(new Runnable() {
- @Override
- public void run() {
- mFuture.release();
- }
- }, FUTURE_RELEASE_DELAY);
+ handler.postDelayed(mFuture::release, FUTURE_RELEASE_DELAY);
}
- };
-
- public EventsTest() {
- super(BasicAnimatorActivity.class);
}
/**
@@ -201,13 +205,12 @@
* and then call super.setup(), where further properties are set on that animator.
* @throws Exception
*/
- @Override
+ @CallSuper
+ @Before
public void setUp() throws Exception {
- super.setUp();
-
// mListener is the main testing mechanism of this file. The asserts of each test
// are embedded in the listener callbacks that it implements.
- mListener = new AnimatorListenerAdapter() {
+ final Animator.AnimatorListener listener = new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
// This should only be called on an animation that has not yet been started
@@ -236,9 +239,8 @@
}
};
- mAnimator.addListener(mListener);
+ mAnimator.addListener(listener);
mAnimator.setDuration(ANIM_DURATION);
-
mFuture = new FutureWaiter();
mRunning = false;
@@ -251,6 +253,7 @@
*/
@UiThreadTest
@SmallTest
+ @Test
public void testCancel() throws Exception {
mAnimator.cancel();
}
@@ -260,6 +263,7 @@
*/
@UiThreadTest
@SmallTest
+ @Test
public void testEnd() throws Exception {
mRunning = true; // end() implicitly starts an unstarted animator
mAnimator.end();
@@ -270,19 +274,17 @@
*/
@UiThreadTest
@SmallTest
- public void testStartCancel() throws Exception {
+ @Test
+ public void testStartCancel() throws Throwable {
mFutureListener = new FutureReleaseListener(mFuture);
- getActivity().runOnUiThread(new Runnable() {
- @Override
- public void run() {
- try {
- mRunning = true;
- mAnimator.start();
- mAnimator.cancel();
- mFuture.release();
- } catch (junit.framework.AssertionFailedError e) {
- mFuture.setException(new RuntimeException(e));
- }
+ mActivityRule.runOnUiThread(() -> {
+ try {
+ mRunning = true;
+ mAnimator.start();
+ mAnimator.cancel();
+ mFuture.release();
+ } catch (junit.framework.AssertionFailedError e) {
+ mFuture.setException(new RuntimeException(e));
}
});
mFuture.get(getTimeout(), TimeUnit.MILLISECONDS);
@@ -293,19 +295,17 @@
*/
@UiThreadTest
@SmallTest
- public void testStartEnd() throws Exception {
+ @Test
+ public void testStartEnd() throws Throwable {
mFutureListener = new FutureReleaseListener(mFuture);
- getActivity().runOnUiThread(new Runnable() {
- @Override
- public void run() {
- try {
- mRunning = true;
- mAnimator.start();
- mAnimator.end();
- mFuture.release();
- } catch (junit.framework.AssertionFailedError e) {
- mFuture.setException(new RuntimeException(e));
- }
+ mActivityRule.runOnUiThread(() -> {
+ try {
+ mRunning = true;
+ mAnimator.start();
+ mAnimator.end();
+ mFuture.release();
+ } catch (junit.framework.AssertionFailedError e) {
+ mFuture.setException(new RuntimeException(e));
}
});
mFuture.get(getTimeout(), TimeUnit.MILLISECONDS);
@@ -315,20 +315,18 @@
* Same as testStartCancel, but with a startDelayed animator
*/
@SmallTest
- public void testStartDelayedCancel() throws Exception {
+ @Test
+ public void testStartDelayedCancel() throws Throwable {
mFutureListener = new FutureReleaseListener(mFuture);
mAnimator.setStartDelay(ANIM_DELAY);
- getActivity().runOnUiThread(new Runnable() {
- @Override
- public void run() {
- try {
- mRunning = true;
- mAnimator.start();
- mAnimator.cancel();
- mFuture.release();
- } catch (junit.framework.AssertionFailedError e) {
- mFuture.setException(new RuntimeException(e));
- }
+ mActivityRule.runOnUiThread(() -> {
+ try {
+ mRunning = true;
+ mAnimator.start();
+ mAnimator.cancel();
+ mFuture.release();
+ } catch (junit.framework.AssertionFailedError e) {
+ mFuture.setException(new RuntimeException(e));
}
});
mFuture.get(getTimeout(), TimeUnit.MILLISECONDS);
@@ -338,20 +336,18 @@
* Same as testStartEnd, but with a startDelayed animator
*/
@SmallTest
- public void testStartDelayedEnd() throws Exception {
+ @Test
+ public void testStartDelayedEnd() throws Throwable {
mFutureListener = new FutureReleaseListener(mFuture);
mAnimator.setStartDelay(ANIM_DELAY);
- getActivity().runOnUiThread(new Runnable() {
- @Override
- public void run() {
- try {
- mRunning = true;
- mAnimator.start();
- mAnimator.end();
- mFuture.release();
- } catch (junit.framework.AssertionFailedError e) {
- mFuture.setException(new RuntimeException(e));
- }
+ mActivityRule.runOnUiThread(() -> {
+ try {
+ mRunning = true;
+ mAnimator.start();
+ mAnimator.end();
+ mFuture.release();
+ } catch (junit.framework.AssertionFailedError e) {
+ mFuture.setException(new RuntimeException(e));
}
});
mFuture.get(getTimeout(), TimeUnit.MILLISECONDS);
@@ -361,20 +357,18 @@
* Verify that canceling an animator that is playing does the right thing.
*/
@MediumTest
- public void testPlayingCancel() throws Exception {
+ @Test
+ public void testPlayingCancel() throws Throwable {
mFutureListener = new FutureReleaseListener(mFuture);
- getActivity().runOnUiThread(new Runnable() {
- @Override
- public void run() {
- try {
- Handler handler = new Handler();
- mAnimator.addListener(mFutureListener);
- mRunning = true;
- mAnimator.start();
- handler.postDelayed(new Canceler(mAnimator, mFuture), ANIM_MID_DURATION);
- } catch (junit.framework.AssertionFailedError e) {
- mFuture.setException(new RuntimeException(e));
- }
+ mActivityRule.runOnUiThread(() -> {
+ try {
+ Handler handler = new Handler();
+ mAnimator.addListener(mFutureListener);
+ mRunning = true;
+ mAnimator.start();
+ handler.postDelayed(new Canceler(mAnimator, mFuture), ANIM_MID_DURATION);
+ } catch (junit.framework.AssertionFailedError e) {
+ mFuture.setException(new RuntimeException(e));
}
});
mFuture.get(getTimeout(), TimeUnit.MILLISECONDS);
@@ -384,20 +378,18 @@
* Verify that ending an animator that is playing does the right thing.
*/
@MediumTest
- public void testPlayingEnd() throws Exception {
+ @Test
+ public void testPlayingEnd() throws Throwable {
mFutureListener = new FutureReleaseListener(mFuture);
- getActivity().runOnUiThread(new Runnable() {
- @Override
- public void run() {
- try {
- Handler handler = new Handler();
- mAnimator.addListener(mFutureListener);
- mRunning = true;
- mAnimator.start();
- handler.postDelayed(new Ender(mAnimator, mFuture), ANIM_MID_DURATION);
- } catch (junit.framework.AssertionFailedError e) {
- mFuture.setException(new RuntimeException(e));
- }
+ mActivityRule.runOnUiThread(() -> {
+ try {
+ Handler handler = new Handler();
+ mAnimator.addListener(mFutureListener);
+ mRunning = true;
+ mAnimator.start();
+ handler.postDelayed(new Ender(mAnimator, mFuture), ANIM_MID_DURATION);
+ } catch (junit.framework.AssertionFailedError e) {
+ mFuture.setException(new RuntimeException(e));
}
});
mFuture.get(getTimeout(), TimeUnit.MILLISECONDS);
@@ -407,21 +399,19 @@
* Same as testPlayingCancel, but with a startDelayed animator
*/
@MediumTest
- public void testPlayingDelayedCancel() throws Exception {
+ @Test
+ public void testPlayingDelayedCancel() throws Throwable {
mAnimator.setStartDelay(ANIM_DELAY);
mFutureListener = new FutureReleaseListener(mFuture);
- getActivity().runOnUiThread(new Runnable() {
- @Override
- public void run() {
- try {
- Handler handler = new Handler();
- mAnimator.addListener(mFutureListener);
- mRunning = true;
- mAnimator.start();
- handler.postDelayed(new Canceler(mAnimator, mFuture), ANIM_MID_DURATION);
- } catch (junit.framework.AssertionFailedError e) {
- mFuture.setException(new RuntimeException(e));
- }
+ mActivityRule.runOnUiThread(() -> {
+ try {
+ Handler handler = new Handler();
+ mAnimator.addListener(mFutureListener);
+ mRunning = true;
+ mAnimator.start();
+ handler.postDelayed(new Canceler(mAnimator, mFuture), ANIM_MID_DURATION);
+ } catch (junit.framework.AssertionFailedError e) {
+ mFuture.setException(new RuntimeException(e));
}
});
mFuture.get(getTimeout(), TimeUnit.MILLISECONDS);
@@ -431,21 +421,19 @@
* Same as testPlayingEnd, but with a startDelayed animator
*/
@MediumTest
- public void testPlayingDelayedEnd() throws Exception {
+ @Test
+ public void testPlayingDelayedEnd() throws Throwable {
mAnimator.setStartDelay(ANIM_DELAY);
mFutureListener = new FutureReleaseListener(mFuture);
- getActivity().runOnUiThread(new Runnable() {
- @Override
- public void run() {
- try {
- Handler handler = new Handler();
- mAnimator.addListener(mFutureListener);
- mRunning = true;
- mAnimator.start();
- handler.postDelayed(new Ender(mAnimator, mFuture), ANIM_MID_DURATION);
- } catch (junit.framework.AssertionFailedError e) {
- mFuture.setException(new RuntimeException(e));
- }
+ mActivityRule.runOnUiThread(() -> {
+ try {
+ Handler handler = new Handler();
+ mAnimator.addListener(mFutureListener);
+ mRunning = true;
+ mAnimator.start();
+ handler.postDelayed(new Ender(mAnimator, mFuture), ANIM_MID_DURATION);
+ } catch (junit.framework.AssertionFailedError e) {
+ mFuture.setException(new RuntimeException(e));
}
});
mFuture.get(getTimeout(), TimeUnit.MILLISECONDS);
@@ -455,24 +443,21 @@
* Same as testPlayingDelayedCancel, but cancel during the startDelay period
*/
@MediumTest
- public void testPlayingDelayedCancelMidDelay() throws Exception {
+ @Test
+ public void testPlayingDelayedCancelMidDelay() throws Throwable {
mAnimator.setStartDelay(ANIM_DELAY);
- getActivity().runOnUiThread(new Runnable() {
- @Override
- public void run() {
- try {
- // Set the listener to automatically timeout after an uncanceled animation
- // would have finished. This tests to make sure that we're not calling
- // the listeners with cancel/end callbacks since they won't be called
- // with the start event.
- mFutureListener = new FutureReleaseListener(mFuture, getTimeout());
- Handler handler = new Handler();
- mRunning = true;
- mAnimator.start();
- handler.postDelayed(new Canceler(mAnimator, mFuture), ANIM_MID_DELAY);
- } catch (junit.framework.AssertionFailedError e) {
- mFuture.setException(new RuntimeException(e));
- }
+ mActivityRule.runOnUiThread(() -> {
+ try {
+ // Set the listener to automatically timeout after an uncanceled animation would
+ // have finished. This tests to make sure that we're not calling the listeners with
+ // cancel/end callbacks since they won't be called with the start event.
+ mFutureListener = new FutureReleaseListener(mFuture, getTimeout());
+ Handler handler = new Handler();
+ mRunning = true;
+ mAnimator.start();
+ handler.postDelayed(new Canceler(mAnimator, mFuture), ANIM_MID_DELAY);
+ } catch (junit.framework.AssertionFailedError e) {
+ mFuture.setException(new RuntimeException(e));
}
});
mFuture.get(getTimeout() + 100, TimeUnit.MILLISECONDS);
@@ -482,24 +467,21 @@
* Same as testPlayingDelayedEnd, but end during the startDelay period
*/
@MediumTest
- public void testPlayingDelayedEndMidDelay() throws Exception {
+ @Test
+ public void testPlayingDelayedEndMidDelay() throws Throwable {
mAnimator.setStartDelay(ANIM_DELAY);
- getActivity().runOnUiThread(new Runnable() {
- @Override
- public void run() {
- try {
- // Set the listener to automatically timeout after an uncanceled animation
- // would have finished. This tests to make sure that we're not calling
- // the listeners with cancel/end callbacks since they won't be called
- // with the start event.
- mFutureListener = new FutureReleaseListener(mFuture, getTimeout());
- Handler handler = new Handler();
- mRunning = true;
- mAnimator.start();
- handler.postDelayed(new Ender(mAnimator, mFuture), ANIM_MID_DELAY);
- } catch (junit.framework.AssertionFailedError e) {
- mFuture.setException(new RuntimeException(e));
- }
+ mActivityRule.runOnUiThread(() -> {
+ try {
+ // Set the listener to automatically timeout after an uncanceled animation would
+ // have finished. This tests to make sure that we're not calling the listeners with
+ // cancel/end callbacks since they won't be called with the start event.
+ mFutureListener = new FutureReleaseListener(mFuture, getTimeout());
+ Handler handler = new Handler();
+ mRunning = true;
+ mAnimator.start();
+ handler.postDelayed(new Ender(mAnimator, mFuture), ANIM_MID_DELAY);
+ } catch (junit.framework.AssertionFailedError e) {
+ mFuture.setException(new RuntimeException(e));
}
});
mFuture.get(getTimeout() + 100, TimeUnit.MILLISECONDS);
@@ -510,20 +492,18 @@
* does nothing.
*/
@MediumTest
- public void testStartDoubleCancel() throws Exception {
+ @Test
+ public void testStartDoubleCancel() throws Throwable {
mFutureListener = new FutureReleaseListener(mFuture);
- getActivity().runOnUiThread(new Runnable() {
- @Override
- public void run() {
- try {
- mRunning = true;
- mAnimator.start();
- mAnimator.cancel();
- mAnimator.cancel();
- mFuture.release();
- } catch (junit.framework.AssertionFailedError e) {
- mFuture.setException(new RuntimeException(e));
- }
+ mActivityRule.runOnUiThread(() -> {
+ try {
+ mRunning = true;
+ mAnimator.start();
+ mAnimator.cancel();
+ mAnimator.cancel();
+ mFuture.release();
+ } catch (junit.framework.AssertionFailedError e) {
+ mFuture.setException(new RuntimeException(e));
}
});
mFuture.get(getTimeout(), TimeUnit.MILLISECONDS);
@@ -534,21 +514,19 @@
* does nothing.
*/
@MediumTest
- public void testStartDoubleEnd() throws Exception {
+ @Test
+ public void testStartDoubleEnd() throws Throwable {
mFutureListener = new FutureReleaseListener(mFuture);
- getActivity().runOnUiThread(new Runnable() {
- @Override
- public void run() {
- try {
- mRunning = true;
- mAnimator.start();
- mAnimator.end();
- mRunning = true; // end() implicitly starts an unstarted animator
- mAnimator.end();
- mFuture.release();
- } catch (junit.framework.AssertionFailedError e) {
- mFuture.setException(new RuntimeException(e));
- }
+ mActivityRule.runOnUiThread(() -> {
+ try {
+ mRunning = true;
+ mAnimator.start();
+ mAnimator.end();
+ mRunning = true; // end() implicitly starts an unstarted animator
+ mAnimator.end();
+ mFuture.release();
+ } catch (junit.framework.AssertionFailedError e) {
+ mFuture.setException(new RuntimeException(e));
}
});
mFuture.get(getTimeout(), TimeUnit.MILLISECONDS);
@@ -558,21 +536,19 @@
* Same as testStartDoubleCancel, but with a startDelayed animator
*/
@MediumTest
- public void testStartDelayedDoubleCancel() throws Exception {
+ @Test
+ public void testStartDelayedDoubleCancel() throws Throwable {
mAnimator.setStartDelay(ANIM_DELAY);
mFutureListener = new FutureReleaseListener(mFuture);
- getActivity().runOnUiThread(new Runnable() {
- @Override
- public void run() {
- try {
- mRunning = true;
- mAnimator.start();
- mAnimator.cancel();
- mAnimator.cancel();
- mFuture.release();
- } catch (junit.framework.AssertionFailedError e) {
- mFuture.setException(new RuntimeException(e));
- }
+ mActivityRule.runOnUiThread(() -> {
+ try {
+ mRunning = true;
+ mAnimator.start();
+ mAnimator.cancel();
+ mAnimator.cancel();
+ mFuture.release();
+ } catch (junit.framework.AssertionFailedError e) {
+ mFuture.setException(new RuntimeException(e));
}
});
mFuture.get(getTimeout(), TimeUnit.MILLISECONDS);
@@ -582,22 +558,20 @@
* Same as testStartDoubleEnd, but with a startDelayed animator
*/
@MediumTest
- public void testStartDelayedDoubleEnd() throws Exception {
+ @Test
+ public void testStartDelayedDoubleEnd() throws Throwable {
mAnimator.setStartDelay(ANIM_DELAY);
mFutureListener = new FutureReleaseListener(mFuture);
- getActivity().runOnUiThread(new Runnable() {
- @Override
- public void run() {
- try {
- mRunning = true;
- mAnimator.start();
- mAnimator.end();
- mRunning = true; // end() implicitly starts an unstarted animator
- mAnimator.end();
- mFuture.release();
- } catch (junit.framework.AssertionFailedError e) {
- mFuture.setException(new RuntimeException(e));
- }
+ mActivityRule.runOnUiThread(() -> {
+ try {
+ mRunning = true;
+ mAnimator.start();
+ mAnimator.end();
+ mRunning = true; // end() implicitly starts an unstarted animator
+ mAnimator.end();
+ mFuture.release();
+ } catch (junit.framework.AssertionFailedError e) {
+ mFuture.setException(new RuntimeException(e));
}
});
mFuture.get(getTimeout(), TimeUnit.MILLISECONDS);
@@ -608,22 +582,20 @@
* the appropriate timeout duration.
*/
@MediumTest
- public void testPauseResume() throws Exception {
+ @Test
+ public void testPauseResume() throws Throwable {
mFutureListener = new FutureReleaseListener(mFuture);
- getActivity().runOnUiThread(new Runnable() {
- @Override
- public void run() {
- try {
- Handler handler = new Handler();
- mAnimator.addListener(mFutureListener);
- mRunning = true;
- mAnimator.start();
- handler.postDelayed(new Pauser(mAnimator, mFuture), ANIM_PAUSE_DELAY);
- handler.postDelayed(new Resumer(mAnimator, mFuture),
- ANIM_PAUSE_DELAY + ANIM_PAUSE_DURATION);
- } catch (junit.framework.AssertionFailedError e) {
- mFuture.setException(new RuntimeException(e));
- }
+ mActivityRule.runOnUiThread(() -> {
+ try {
+ Handler handler = new Handler();
+ mAnimator.addListener(mFutureListener);
+ mRunning = true;
+ mAnimator.start();
+ handler.postDelayed(new Pauser(mAnimator, mFuture), ANIM_PAUSE_DELAY);
+ handler.postDelayed(new Resumer(mAnimator, mFuture),
+ ANIM_PAUSE_DELAY + ANIM_PAUSE_DURATION);
+ } catch (junit.framework.AssertionFailedError e) {
+ mFuture.setException(new RuntimeException(e));
}
});
mFuture.get(getTimeout() + ANIM_PAUSE_DURATION, TimeUnit.MILLISECONDS);
@@ -634,23 +606,21 @@
* the appropriate timeout duration.
*/
@MediumTest
- public void testPauseResumeDelayed() throws Exception {
+ @Test
+ public void testPauseResumeDelayed() throws Throwable {
mAnimator.setStartDelay(ANIM_DELAY);
mFutureListener = new FutureReleaseListener(mFuture);
- getActivity().runOnUiThread(new Runnable() {
- @Override
- public void run() {
- try {
- Handler handler = new Handler();
- mAnimator.addListener(mFutureListener);
- mRunning = true;
- mAnimator.start();
- handler.postDelayed(new Pauser(mAnimator, mFuture), ANIM_PAUSE_DELAY);
- handler.postDelayed(new Resumer(mAnimator, mFuture),
- ANIM_PAUSE_DELAY + ANIM_PAUSE_DURATION);
- } catch (junit.framework.AssertionFailedError e) {
- mFuture.setException(new RuntimeException(e));
- }
+ mActivityRule.runOnUiThread(() -> {
+ try {
+ Handler handler = new Handler();
+ mAnimator.addListener(mFutureListener);
+ mRunning = true;
+ mAnimator.start();
+ handler.postDelayed(new Pauser(mAnimator, mFuture), ANIM_PAUSE_DELAY);
+ handler.postDelayed(new Resumer(mAnimator, mFuture),
+ ANIM_PAUSE_DELAY + ANIM_PAUSE_DURATION);
+ } catch (junit.framework.AssertionFailedError e) {
+ mFuture.setException(new RuntimeException(e));
}
});
mFuture.get(getTimeout() + ANIM_PAUSE_DURATION + ANIM_FULL_DURATION_SLOP,
@@ -661,20 +631,18 @@
* Verify that pausing an animator without resuming it causes a timeout.
*/
@MediumTest
- public void testPauseTimeout() throws Exception {
+ @Test
+ public void testPauseTimeout() throws Throwable {
mFutureListener = new FutureReleaseListener(mFuture);
- getActivity().runOnUiThread(new Runnable() {
- @Override
- public void run() {
- try {
- Handler handler = new Handler();
- mAnimator.addListener(mFutureListener);
- mRunning = true;
- mAnimator.start();
- handler.postDelayed(new Pauser(mAnimator, mFuture), ANIM_PAUSE_DELAY);
- } catch (junit.framework.AssertionFailedError e) {
- mFuture.setException(new RuntimeException(e));
- }
+ mActivityRule.runOnUiThread(() -> {
+ try {
+ Handler handler = new Handler();
+ mAnimator.addListener(mFutureListener);
+ mRunning = true;
+ mAnimator.start();
+ handler.postDelayed(new Pauser(mAnimator, mFuture), ANIM_PAUSE_DELAY);
+ } catch (junit.framework.AssertionFailedError e) {
+ mFuture.setException(new RuntimeException(e));
}
});
try {
@@ -689,21 +657,19 @@
* Verify that pausing a startDelayed animator without resuming it causes a timeout.
*/
@MediumTest
- public void testPauseTimeoutDelayed() throws Exception {
+ @Test
+ public void testPauseTimeoutDelayed() throws Throwable {
mAnimator.setStartDelay(ANIM_DELAY);
mFutureListener = new FutureReleaseListener(mFuture);
- getActivity().runOnUiThread(new Runnable() {
- @Override
- public void run() {
- try {
- Handler handler = new Handler();
- mAnimator.addListener(mFutureListener);
- mRunning = true;
- mAnimator.start();
- handler.postDelayed(new Pauser(mAnimator, mFuture), ANIM_PAUSE_DELAY);
- } catch (junit.framework.AssertionFailedError e) {
- mFuture.setException(new RuntimeException(e));
- }
+ mActivityRule.runOnUiThread(() -> {
+ try {
+ Handler handler = new Handler();
+ mAnimator.addListener(mFutureListener);
+ mRunning = true;
+ mAnimator.start();
+ handler.postDelayed(new Pauser(mAnimator, mFuture), ANIM_PAUSE_DELAY);
+ } catch (junit.framework.AssertionFailedError e) {
+ mFuture.setException(new RuntimeException(e));
}
});
try {
diff --git a/core/tests/coretests/src/android/animation/ObjectAnimatorEventsTest.java b/core/tests/coretests/src/android/animation/ObjectAnimatorEventsTest.java
index 53f9472..63ad061 100644
--- a/core/tests/coretests/src/android/animation/ObjectAnimatorEventsTest.java
+++ b/core/tests/coretests/src/android/animation/ObjectAnimatorEventsTest.java
@@ -27,11 +27,10 @@
@Override
public void setUp() throws Exception {
- final BasicAnimatorActivity activity = getActivity();
- Button button = (Button) activity.findViewById(R.id.animatingButton);
+ final BasicAnimatorActivity activity = mActivityRule.getActivity();
+ Button button = activity.findViewById(R.id.animatingButton);
mAnimator = ObjectAnimator.ofFloat(button, "translationX", 0, 100);
super.setUp();
}
-
}
diff --git a/core/tests/coretests/src/android/animation/StateListAnimatorTest.java b/core/tests/coretests/src/android/animation/StateListAnimatorTest.java
index e755b89..12f1977 100644
--- a/core/tests/coretests/src/android/animation/StateListAnimatorTest.java
+++ b/core/tests/coretests/src/android/animation/StateListAnimatorTest.java
@@ -16,41 +16,47 @@
package android.animation;
-import android.test.ActivityInstrumentationTestCase2;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
import android.util.StateSet;
import android.view.View;
import android.view.ViewGroup;
import androidx.test.annotation.UiThreadTest;
import androidx.test.filters.LargeTest;
+import androidx.test.rule.ActivityTestRule;
import com.android.frameworks.coretests.R;
+import org.junit.Rule;
+import org.junit.Test;
+
import java.util.concurrent.atomic.AtomicInteger;
@LargeTest
-public class StateListAnimatorTest extends ActivityInstrumentationTestCase2<BasicAnimatorActivity> {
+public class StateListAnimatorTest {
- public StateListAnimatorTest() {
- super(BasicAnimatorActivity.class);
- }
+ @Rule
+ public final ActivityTestRule<BasicAnimatorActivity> mActivityRule =
+ new ActivityTestRule<>(BasicAnimatorActivity.class);
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- }
-
+ @Test
public void testInflateFromAnimator() throws Exception {
StateListAnimator stateListAnimator = AnimatorInflater
- .loadStateListAnimator(getActivity(), R.anim.test_state_anim);
+ .loadStateListAnimator(mActivityRule.getActivity(), R.anim.test_state_anim);
assertNotNull("A state list animator should be returned", stateListAnimator);
assertEquals("State list animator should have three items", 3,
stateListAnimator.getTuples().size());
}
@UiThreadTest
+ @Test
public void testAttachDetach() throws Exception {
- View view = new View(getActivity());
+ final BasicAnimatorActivity activity = mActivityRule.getActivity();
+ View view = new View(activity);
final AtomicInteger setStateCount = new AtomicInteger(0);
StateListAnimator stateListAnimator = new StateListAnimator() {
@Override
@@ -62,7 +68,7 @@
view.setStateListAnimator(stateListAnimator);
assertNotNull("State list animator should have a reference to view even if it is detached",
stateListAnimator.getTarget());
- ViewGroup viewGroup = (ViewGroup) getActivity().findViewById(android.R.id.content);
+ ViewGroup viewGroup = activity.findViewById(android.R.id.content);
int preSetStateCount = setStateCount.get();
viewGroup.addView(view);
assertTrue("When view is attached, state list drawable's setState should be called",
@@ -82,9 +88,10 @@
stateListAnimator2.getTarget());
}
+ @Test
public void testStateListLoading() throws InterruptedException {
StateListAnimator stateListAnimator = AnimatorInflater
- .loadStateListAnimator(getActivity(), R.anim.test_state_anim);
+ .loadStateListAnimator(mActivityRule.getActivity(), R.anim.test_state_anim);
assertNotNull("A state list animator should be returned", stateListAnimator);
assertEquals("Steate list animator should have two items", 3,
stateListAnimator.getTuples().size());
diff --git a/core/tests/coretests/src/android/animation/ValueAnimatorEventsTest.java b/core/tests/coretests/src/android/animation/ValueAnimatorEventsTest.java
index f6d71b8..ba9aef8 100644
--- a/core/tests/coretests/src/android/animation/ValueAnimatorEventsTest.java
+++ b/core/tests/coretests/src/android/animation/ValueAnimatorEventsTest.java
@@ -26,5 +26,4 @@
mAnimator = ValueAnimator.ofFloat(0, 1);
super.setUp();
}
-
}
diff --git a/core/tests/coretests/src/android/animation/ViewPropertyAnimatorTest.java b/core/tests/coretests/src/android/animation/ViewPropertyAnimatorTest.java
index 997af00..81cd4da 100644
--- a/core/tests/coretests/src/android/animation/ViewPropertyAnimatorTest.java
+++ b/core/tests/coretests/src/android/animation/ViewPropertyAnimatorTest.java
@@ -16,17 +16,24 @@
package android.animation;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
import android.os.Handler;
-import android.test.ActivityInstrumentationTestCase2;
import android.view.ViewPropertyAnimator;
import android.widget.Button;
import androidx.test.annotation.UiThreadTest;
import androidx.test.filters.MediumTest;
import androidx.test.filters.SmallTest;
+import androidx.test.rule.ActivityTestRule;
import com.android.frameworks.coretests.R;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+
import java.util.concurrent.TimeUnit;
/**
@@ -41,8 +48,11 @@
* wait for some later event to occur before ending. These tests use a combination of an
* AbstractFuture mechanism and a delayed action to release that Future later.
*/
-public abstract class ViewPropertyAnimatorTest
- extends ActivityInstrumentationTestCase2<BasicAnimatorActivity> {
+public class ViewPropertyAnimatorTest {
+
+ @Rule
+ public final ActivityTestRule<BasicAnimatorActivity> mActivityRule =
+ new ActivityTestRule<>(BasicAnimatorActivity.class);
protected static final int ANIM_DURATION = 400;
protected static final int ANIM_DELAY = 100;
@@ -79,7 +89,7 @@
mFuture.setException(new RuntimeException(e));
}
}
- };
+ }
/**
* Timeout length, based on when the animation should reasonably be complete.
@@ -108,28 +118,14 @@
public FutureReleaseListener(FutureWaiter future, long timeout) {
mFuture = future;
Handler handler = new Handler();
- handler.postDelayed(new Runnable() {
- @Override
- public void run() {
- mFuture.release();
- }
- }, timeout);
+ handler.postDelayed(mFuture::release, timeout);
}
@Override
public void onAnimationEnd(Animator animation) {
Handler handler = new Handler();
- handler.postDelayed(new Runnable() {
- @Override
- public void run() {
- mFuture.release();
- }
- }, FUTURE_RELEASE_DELAY);
+ handler.postDelayed(mFuture::release, FUTURE_RELEASE_DELAY);
}
- };
-
- public ViewPropertyAnimatorTest() {
- super(BasicAnimatorActivity.class);
}
/**
@@ -138,15 +134,13 @@
* and then call super.setup(), where further properties are set on that animator.
* @throws Exception
*/
- @Override
+ @Before
public void setUp() throws Exception {
- final BasicAnimatorActivity activity = getActivity();
- Button button = (Button) activity.findViewById(R.id.animatingButton);
+ final BasicAnimatorActivity activity = mActivityRule.getActivity();
+ Button button = activity.findViewById(R.id.animatingButton);
mAnimator = button.animate().x(100).y(100);
- super.setUp();
-
// mListener is the main testing mechanism of this file. The asserts of each test
// are embedded in the listener callbacks that it implements.
mListener = new AnimatorListenerAdapter() {
@@ -195,6 +189,7 @@
*/
@UiThreadTest
@SmallTest
+ @Test
public void testCancel() throws Exception {
mAnimator.cancel();
}
@@ -204,19 +199,17 @@
*/
@UiThreadTest
@SmallTest
- public void testStartCancel() throws Exception {
+ @Test
+ public void testStartCancel() throws Throwable {
mFutureListener = new FutureReleaseListener(mFuture);
- getActivity().runOnUiThread(new Runnable() {
- @Override
- public void run() {
- try {
- mRunning = true;
- mAnimator.start();
- mAnimator.cancel();
- mFuture.release();
- } catch (junit.framework.AssertionFailedError e) {
- mFuture.setException(new RuntimeException(e));
- }
+ mActivityRule.runOnUiThread(() -> {
+ try {
+ mRunning = true;
+ mAnimator.start();
+ mAnimator.cancel();
+ mFuture.release();
+ } catch (junit.framework.AssertionFailedError e) {
+ mFuture.setException(new RuntimeException(e));
}
});
mFuture.get(getTimeout(), TimeUnit.MILLISECONDS);
@@ -226,20 +219,18 @@
* Same as testStartCancel, but with a startDelayed animator
*/
@SmallTest
- public void testStartDelayedCancel() throws Exception {
+ @Test
+ public void testStartDelayedCancel() throws Throwable {
mFutureListener = new FutureReleaseListener(mFuture);
mAnimator.setStartDelay(ANIM_DELAY);
- getActivity().runOnUiThread(new Runnable() {
- @Override
- public void run() {
- try {
- mRunning = true;
- mAnimator.start();
- mAnimator.cancel();
- mFuture.release();
- } catch (junit.framework.AssertionFailedError e) {
- mFuture.setException(new RuntimeException(e));
- }
+ mActivityRule.runOnUiThread(() -> {
+ try {
+ mRunning = true;
+ mAnimator.start();
+ mAnimator.cancel();
+ mFuture.release();
+ } catch (junit.framework.AssertionFailedError e) {
+ mFuture.setException(new RuntimeException(e));
}
});
mFuture.get(getTimeout(), TimeUnit.MILLISECONDS);
@@ -249,20 +240,18 @@
* Verify that canceling an animator that is playing does the right thing.
*/
@MediumTest
- public void testPlayingCancel() throws Exception {
+ @Test
+ public void testPlayingCancel() throws Throwable {
mFutureListener = new FutureReleaseListener(mFuture);
- getActivity().runOnUiThread(new Runnable() {
- @Override
- public void run() {
- try {
- Handler handler = new Handler();
- mAnimator.setListener(mFutureListener);
- mRunning = true;
- mAnimator.start();
- handler.postDelayed(new Canceler(mAnimator, mFuture), ANIM_MID_DURATION);
- } catch (junit.framework.AssertionFailedError e) {
- mFuture.setException(new RuntimeException(e));
- }
+ mActivityRule.runOnUiThread(() -> {
+ try {
+ Handler handler = new Handler();
+ mAnimator.setListener(mFutureListener);
+ mRunning = true;
+ mAnimator.start();
+ handler.postDelayed(new Canceler(mAnimator, mFuture), ANIM_MID_DURATION);
+ } catch (junit.framework.AssertionFailedError e) {
+ mFuture.setException(new RuntimeException(e));
}
});
mFuture.get(getTimeout(), TimeUnit.MILLISECONDS);
@@ -272,21 +261,19 @@
* Same as testPlayingCancel, but with a startDelayed animator
*/
@MediumTest
- public void testPlayingDelayedCancel() throws Exception {
+ @Test
+ public void testPlayingDelayedCancel() throws Throwable {
mAnimator.setStartDelay(ANIM_DELAY);
mFutureListener = new FutureReleaseListener(mFuture);
- getActivity().runOnUiThread(new Runnable() {
- @Override
- public void run() {
- try {
- Handler handler = new Handler();
- mAnimator.setListener(mFutureListener);
- mRunning = true;
- mAnimator.start();
- handler.postDelayed(new Canceler(mAnimator, mFuture), ANIM_MID_DURATION);
- } catch (junit.framework.AssertionFailedError e) {
- mFuture.setException(new RuntimeException(e));
- }
+ mActivityRule.runOnUiThread(() -> {
+ try {
+ Handler handler = new Handler();
+ mAnimator.setListener(mFutureListener);
+ mRunning = true;
+ mAnimator.start();
+ handler.postDelayed(new Canceler(mAnimator, mFuture), ANIM_MID_DURATION);
+ } catch (junit.framework.AssertionFailedError e) {
+ mFuture.setException(new RuntimeException(e));
}
});
mFuture.get(getTimeout(), TimeUnit.MILLISECONDS);
@@ -296,24 +283,21 @@
* Same as testPlayingDelayedCancel, but cancel during the startDelay period
*/
@MediumTest
- public void testPlayingDelayedCancelMidDelay() throws Exception {
+ @Test
+ public void testPlayingDelayedCancelMidDelay() throws Throwable {
mAnimator.setStartDelay(ANIM_DELAY);
- getActivity().runOnUiThread(new Runnable() {
- @Override
- public void run() {
- try {
- // Set the listener to automatically timeout after an uncanceled animation
- // would have finished. This tests to make sure that we're not calling
- // the listeners with cancel/end callbacks since they won't be called
- // with the start event.
- mFutureListener = new FutureReleaseListener(mFuture, getTimeout());
- Handler handler = new Handler();
- mRunning = true;
- mAnimator.start();
- handler.postDelayed(new Canceler(mAnimator, mFuture), ANIM_MID_DELAY);
- } catch (junit.framework.AssertionFailedError e) {
- mFuture.setException(new RuntimeException(e));
- }
+ mActivityRule.runOnUiThread(() -> {
+ try {
+ // Set the listener to automatically timeout after an uncanceled animation would
+ // have finished. This tests to make sure that we're not calling the listeners with
+ // cancel/end callbacks since they won't be called with the start event.
+ mFutureListener = new FutureReleaseListener(mFuture, getTimeout());
+ Handler handler = new Handler();
+ mRunning = true;
+ mAnimator.start();
+ handler.postDelayed(new Canceler(mAnimator, mFuture), ANIM_MID_DELAY);
+ } catch (junit.framework.AssertionFailedError e) {
+ mFuture.setException(new RuntimeException(e));
}
});
mFuture.get(getTimeout() + 100, TimeUnit.MILLISECONDS);
@@ -324,20 +308,18 @@
* does nothing.
*/
@MediumTest
- public void testStartDoubleCancel() throws Exception {
+ @Test
+ public void testStartDoubleCancel() throws Throwable {
mFutureListener = new FutureReleaseListener(mFuture);
- getActivity().runOnUiThread(new Runnable() {
- @Override
- public void run() {
- try {
- mRunning = true;
- mAnimator.start();
- mAnimator.cancel();
- mAnimator.cancel();
- mFuture.release();
- } catch (junit.framework.AssertionFailedError e) {
- mFuture.setException(new RuntimeException(e));
- }
+ mActivityRule.runOnUiThread(() -> {
+ try {
+ mRunning = true;
+ mAnimator.start();
+ mAnimator.cancel();
+ mAnimator.cancel();
+ mFuture.release();
+ } catch (junit.framework.AssertionFailedError e) {
+ mFuture.setException(new RuntimeException(e));
}
});
mFuture.get(getTimeout(), TimeUnit.MILLISECONDS);
@@ -347,24 +329,21 @@
* Same as testStartDoubleCancel, but with a startDelayed animator
*/
@MediumTest
- public void testStartDelayedDoubleCancel() throws Exception {
+ @Test
+ public void testStartDelayedDoubleCancel() throws Throwable {
mAnimator.setStartDelay(ANIM_DELAY);
mFutureListener = new FutureReleaseListener(mFuture);
- getActivity().runOnUiThread(new Runnable() {
- @Override
- public void run() {
- try {
- mRunning = true;
- mAnimator.start();
- mAnimator.cancel();
- mAnimator.cancel();
- mFuture.release();
- } catch (junit.framework.AssertionFailedError e) {
- mFuture.setException(new RuntimeException(e));
- }
+ mActivityRule.runOnUiThread(() -> {
+ try {
+ mRunning = true;
+ mAnimator.start();
+ mAnimator.cancel();
+ mAnimator.cancel();
+ mFuture.release();
+ } catch (junit.framework.AssertionFailedError e) {
+ mFuture.setException(new RuntimeException(e));
}
});
mFuture.get(getTimeout(), TimeUnit.MILLISECONDS);
}
-
}
diff --git a/core/tests/coretests/src/android/hardware/hdmi/HdmiUtilsTest.java b/core/tests/coretests/src/android/hardware/hdmi/HdmiUtilsTest.java
index 16be0b0..d8799cb 100644
--- a/core/tests/coretests/src/android/hardware/hdmi/HdmiUtilsTest.java
+++ b/core/tests/coretests/src/android/hardware/hdmi/HdmiUtilsTest.java
@@ -17,7 +17,7 @@
import static com.google.common.truth.Truth.assertThat;
-import android.support.test.filters.SmallTest;
+import androidx.test.filters.SmallTest;
import org.junit.Test;
import org.junit.runner.RunWith;
diff --git a/core/tests/coretests/src/android/view/AccessibilityInteractionControllerTest.java b/core/tests/coretests/src/android/view/AccessibilityInteractionControllerTest.java
index d0719cb..7855ef9 100644
--- a/core/tests/coretests/src/android/view/AccessibilityInteractionControllerTest.java
+++ b/core/tests/coretests/src/android/view/AccessibilityInteractionControllerTest.java
@@ -26,9 +26,6 @@
import android.app.UiAutomation;
import android.graphics.Rect;
import android.os.SystemClock;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.rule.ActivityTestRule;
-import android.support.test.runner.AndroidJUnit4;
import android.text.TextUtils;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
@@ -36,6 +33,10 @@
import android.view.accessibility.AccessibilityTestActivity;
import android.view.accessibility.AccessibilityWindowInfo;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.rule.ActivityTestRule;
+
import com.android.compatibility.common.util.TestUtils;
import com.android.frameworks.coretests.R;
diff --git a/core/tests/coretests/src/android/view/PinchZoomAction.java b/core/tests/coretests/src/android/view/PinchZoomAction.java
index bec9b55..cfdec4d 100644
--- a/core/tests/coretests/src/android/view/PinchZoomAction.java
+++ b/core/tests/coretests/src/android/view/PinchZoomAction.java
@@ -16,20 +16,21 @@
package android.view;
-import static android.support.test.espresso.matcher.ViewMatchers.isAssignableFrom;
-import static android.support.test.espresso.matcher.ViewMatchers.isCompletelyDisplayed;
+import static androidx.test.espresso.matcher.ViewMatchers.isAssignableFrom;
+import static androidx.test.espresso.matcher.ViewMatchers.isCompletelyDisplayed;
import static com.google.common.base.Preconditions.checkNotNull;
import static org.hamcrest.Matchers.allOf;
import android.os.SystemClock;
-import android.support.test.espresso.InjectEventSecurityException;
-import android.support.test.espresso.PerformException;
-import android.support.test.espresso.UiController;
-import android.support.test.espresso.ViewAction;
-import android.support.test.espresso.action.Swiper;
-import android.support.test.espresso.util.HumanReadables;
+
+import androidx.test.espresso.InjectEventSecurityException;
+import androidx.test.espresso.PerformException;
+import androidx.test.espresso.UiController;
+import androidx.test.espresso.ViewAction;
+import androidx.test.espresso.action.Swiper;
+import androidx.test.espresso.util.HumanReadables;
import org.hamcrest.Matcher;
diff --git a/core/tests/coretests/src/android/view/ScaleGestureDetectorTest.java b/core/tests/coretests/src/android/view/ScaleGestureDetectorTest.java
index 1990135..f63a454 100644
--- a/core/tests/coretests/src/android/view/ScaleGestureDetectorTest.java
+++ b/core/tests/coretests/src/android/view/ScaleGestureDetectorTest.java
@@ -16,48 +16,43 @@
package android.view;
-import static android.support.test.espresso.Espresso.onView;
-import static android.support.test.espresso.matcher.ViewMatchers.withId;
+import static androidx.test.espresso.Espresso.onView;
+import static androidx.test.espresso.matcher.ViewMatchers.withId;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
import android.content.Context;
-import android.test.ActivityInstrumentationTestCase2;
import android.util.DisplayMetrics;
import android.widget.TextView;
-import androidx.test.InstrumentationRegistry;
import androidx.test.filters.LargeTest;
+import androidx.test.rule.ActivityTestRule;
import com.android.frameworks.coretests.R;
-import org.junit.After;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
@LargeTest
-public class ScaleGestureDetectorTest extends ActivityInstrumentationTestCase2<ScaleGesture> {
- private ScaleGesture mScaleGestureActivity;
+public class ScaleGestureDetectorTest {
- public ScaleGestureDetectorTest() {
- super("com.android.frameworks.coretests", ScaleGesture.class);
- }
+ @Rule
+ public final ActivityTestRule<ScaleGesture> mActivityRule =
+ new ActivityTestRule<>(ScaleGesture.class);
+ private ScaleGesture mScaleGestureActivity;
@Before
public void setUp() throws Exception {
- super.setUp();
- injectInstrumentation(InstrumentationRegistry.getInstrumentation());
- mScaleGestureActivity = getActivity();
- }
-
- @After
- public void tearDown() throws Exception {
- super.tearDown();
+ mScaleGestureActivity = mActivityRule.getActivity();
}
@Test
public void testScaleGestureDetector() {
// No scaling should have occurred prior to performing pinch zoom action.
final float initialScaleFactor = 1.0f;
- assertEquals(initialScaleFactor, mScaleGestureActivity.getScaleFactor());
+ assertEquals(initialScaleFactor, mScaleGestureActivity.getScaleFactor(), 0f);
// Specify start and end coordinates, irrespective of device display size.
final DisplayMetrics dm = new DisplayMetrics();
diff --git a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java
index 71612e6..34fdebf 100644
--- a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java
+++ b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java
@@ -155,5 +155,10 @@
void internalNotifyViewTextChanged(AutofillId id, CharSequence text) {
throw new UnsupportedOperationException("should not have been called");
}
+
+ @Override
+ public void internalNotifyViewHierarchyEvent(boolean started) {
+ throw new UnsupportedOperationException("should not have been called");
+ }
}
}
diff --git a/core/tests/coretests/src/android/widget/EditorCursorTest.java b/core/tests/coretests/src/android/widget/EditorCursorTest.java
index e4f55df..585c601 100644
--- a/core/tests/coretests/src/android/widget/EditorCursorTest.java
+++ b/core/tests/coretests/src/android/widget/EditorCursorTest.java
@@ -16,11 +16,12 @@
package android.widget;
-import static android.support.test.espresso.Espresso.onView;
-import static android.support.test.espresso.action.ViewActions.click;
import static android.widget.espresso.TextViewAssertions.hasInsertionPointerOnLeft;
import static android.widget.espresso.TextViewAssertions.hasInsertionPointerOnRight;
+import static androidx.test.espresso.Espresso.onView;
+import static androidx.test.espresso.action.ViewActions.click;
+
import static junit.framework.Assert.fail;
import static org.hamcrest.MatcherAssert.assertThat;
diff --git a/core/tests/coretests/src/android/widget/SuggestionsPopupWindowTest.java b/core/tests/coretests/src/android/widget/SuggestionsPopupWindowTest.java
index 483270e..f6e02bc 100644
--- a/core/tests/coretests/src/android/widget/SuggestionsPopupWindowTest.java
+++ b/core/tests/coretests/src/android/widget/SuggestionsPopupWindowTest.java
@@ -16,15 +16,6 @@
package android.widget;
-import static android.support.test.espresso.Espresso.onView;
-import static android.support.test.espresso.Espresso.pressBack;
-import static android.support.test.espresso.action.ViewActions.clearText;
-import static android.support.test.espresso.action.ViewActions.click;
-import static android.support.test.espresso.action.ViewActions.replaceText;
-import static android.support.test.espresso.assertion.ViewAssertions.matches;
-import static android.support.test.espresso.matcher.RootMatchers.withDecorView;
-import static android.support.test.espresso.matcher.ViewMatchers.withId;
-import static android.support.test.espresso.matcher.ViewMatchers.withText;
import static android.widget.espresso.DragHandleUtils.onHandleView;
import static android.widget.espresso.FloatingToolbarEspressoUtils.assertFloatingToolbarContainsItem;
import static android.widget.espresso.FloatingToolbarEspressoUtils.clickFloatingToolbarItem;
@@ -37,43 +28,55 @@
import static android.widget.espresso.TextViewActions.clickOnTextAtIndex;
import static android.widget.espresso.TextViewActions.longPressOnTextAtIndex;
+import static androidx.test.espresso.Espresso.onView;
+import static androidx.test.espresso.Espresso.pressBack;
+import static androidx.test.espresso.action.ViewActions.clearText;
+import static androidx.test.espresso.action.ViewActions.click;
+import static androidx.test.espresso.action.ViewActions.replaceText;
+import static androidx.test.espresso.assertion.ViewAssertions.matches;
+import static androidx.test.espresso.matcher.RootMatchers.withDecorView;
+import static androidx.test.espresso.matcher.ViewMatchers.withId;
+import static androidx.test.espresso.matcher.ViewMatchers.withText;
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
import android.content.res.TypedArray;
-import android.support.test.espresso.NoMatchingViewException;
-import android.support.test.espresso.ViewAssertion;
-import android.test.ActivityInstrumentationTestCase2;
import android.text.Selection;
import android.text.Spannable;
import android.text.Spanned;
import android.text.TextPaint;
import android.text.style.SuggestionSpan;
import android.text.style.TextAppearanceSpan;
-import android.view.View;
import androidx.test.filters.SmallTest;
+import androidx.test.rule.ActivityTestRule;
import com.android.frameworks.coretests.R;
+import org.junit.Rule;
+import org.junit.Test;
+
/**
* SuggestionsPopupWindowTest tests.
*
* TODO: Add tests for when there are no suggestions
*/
-public class SuggestionsPopupWindowTest extends ActivityInstrumentationTestCase2<TextViewActivity> {
+@SmallTest
+public class SuggestionsPopupWindowTest {
- public SuggestionsPopupWindowTest() {
- super(TextViewActivity.class);
- }
+ @Rule
+ public final ActivityTestRule<TextViewActivity> mActivityRule =
+ new ActivityTestRule<>(TextViewActivity.class);
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- getActivity();
+ private TextViewActivity getActivity() {
+ return mActivityRule.getActivity();
}
private void setSuggestionSpan(SuggestionSpan span, int start, int end) {
- final TextView textView = (TextView) getActivity().findViewById(R.id.textview);
+ final TextView textView = getActivity().findViewById(R.id.textview);
textView.post(
() -> {
final Spannable text = (Spannable) textView.getText();
@@ -83,7 +86,7 @@
getInstrumentation().waitForIdleSync();
}
- @SmallTest
+ @Test
public void testOnTextContextMenuItem() {
final String text = "abc def ghi";
@@ -94,14 +97,14 @@
new String[]{"DEF", "Def"}, SuggestionSpan.FLAG_AUTO_CORRECTION);
setSuggestionSpan(suggestionSpan, text.indexOf('d'), text.indexOf('f') + 1);
- final TextView textView = (TextView) getActivity().findViewById(R.id.textview);
+ final TextView textView = getActivity().findViewById(R.id.textview);
textView.post(() -> textView.onTextContextMenuItem(TextView.ID_REPLACE));
getInstrumentation().waitForIdleSync();
assertSuggestionsPopupIsDisplayed();
}
- @SmallTest
+ @Test
public void testSelectionActionMode() {
final String text = "abc def ghi";
@@ -123,7 +126,7 @@
assertSuggestionsPopupIsDisplayed();
}
- @SmallTest
+ @Test
public void testInsertionActionMode() {
final String text = "abc def ghi";
@@ -146,13 +149,13 @@
}
private void showSuggestionsPopup() {
- final TextView textView = (TextView) getActivity().findViewById(R.id.textview);
+ final TextView textView = getActivity().findViewById(R.id.textview);
textView.post(() -> textView.onTextContextMenuItem(TextView.ID_REPLACE));
getInstrumentation().waitForIdleSync();
assertSuggestionsPopupIsDisplayed();
}
- @SmallTest
+ @Test
public void testSuggestionItems() {
final String text = "abc def ghi";
@@ -190,7 +193,7 @@
onView(withId(R.id.textview)).check(matches(withText("abc ghi")));
}
- @SmallTest
+ @Test
public void testMisspelled() {
final String text = "abc def ghi";
@@ -217,7 +220,7 @@
// TODO: Check if add to dictionary dialog is displayed.
}
- @SmallTest
+ @Test
public void testEasyCorrect() {
final String text = "abc def ghi";
@@ -253,7 +256,7 @@
getActivity().getString(com.android.internal.R.string.delete));
}
- @SmallTest
+ @Test
public void testTextAppearanceInSuggestionsPopup() {
final String text = "abc def ghi";
@@ -302,53 +305,49 @@
assertSuggestionsPopupContainsItem(
getActivity().getString(com.android.internal.R.string.delete));
- onSuggestionsPopup().check(new ViewAssertion() {
- @Override
- public void check(View view, NoMatchingViewException e) {
- final ListView listView = (ListView) view.findViewById(
- com.android.internal.R.id.suggestionContainer);
- assertNotNull(listView);
- final int childNum = listView.getChildCount();
- assertEquals(singleWordCandidates.length + multiWordCandidates.length,
- childNum);
+ onSuggestionsPopup().check((view, e) -> {
+ final ListView listView = view.findViewById(
+ com.android.internal.R.id.suggestionContainer);
+ assertNotNull(listView);
+ final int childNum = listView.getChildCount();
+ assertEquals(singleWordCandidates.length + multiWordCandidates.length, childNum);
- for (int j = 0; j < childNum; j++) {
- final TextView suggestion = (TextView) listView.getChildAt(j);
- assertNotNull(suggestion);
- final Spanned spanned = (Spanned) suggestion.getText();
- assertNotNull(spanned);
+ for (int j = 0; j < childNum; j++) {
+ final TextView suggestion = (TextView) listView.getChildAt(j);
+ assertNotNull(suggestion);
+ final Spanned spanned = (Spanned) suggestion.getText();
+ assertNotNull(spanned);
- // Check that the suggestion item order is kept.
- final String expectedText;
- if (j < singleWordCandidates.length) {
- expectedText = "abc " + singleWordCandidates[j] + " ghi";
- } else {
- expectedText = multiWordCandidates[j - singleWordCandidates.length];
- }
- assertEquals(expectedText, spanned.toString());
-
- // Check that the text is highlighted with correct color and text size.
- final TextAppearanceSpan[] taSpan = spanned.getSpans(
- text.indexOf('d'), text.indexOf('f') + 1, TextAppearanceSpan.class);
- assertEquals(1, taSpan.length);
- TextPaint tp = new TextPaint();
- taSpan[0].updateDrawState(tp);
- assertEquals(expectedHighlightTextColor, tp.getColor());
- assertEquals(expectedHighlightTextSize, tp.getTextSize());
-
- // Check the correct part of the text is highlighted.
- final int expectedStart;
- final int expectedEnd;
- if (j < singleWordCandidates.length) {
- expectedStart = text.indexOf('d');
- expectedEnd = text.indexOf('f') + 1;
- } else {
- expectedStart = 0;
- expectedEnd = text.length();
- }
- assertEquals(expectedStart, spanned.getSpanStart(taSpan[0]));
- assertEquals(expectedEnd, spanned.getSpanEnd(taSpan[0]));
+ // Check that the suggestion item order is kept.
+ final String expectedText;
+ if (j < singleWordCandidates.length) {
+ expectedText = "abc " + singleWordCandidates[j] + " ghi";
+ } else {
+ expectedText = multiWordCandidates[j - singleWordCandidates.length];
}
+ assertEquals(expectedText, spanned.toString());
+
+ // Check that the text is highlighted with correct color and text size.
+ final TextAppearanceSpan[] taSpan = spanned.getSpans(
+ text.indexOf('d'), text.indexOf('f') + 1, TextAppearanceSpan.class);
+ assertEquals(1, taSpan.length);
+ TextPaint tp = new TextPaint();
+ taSpan[0].updateDrawState(tp);
+ assertEquals(expectedHighlightTextColor, tp.getColor());
+ assertEquals(expectedHighlightTextSize, tp.getTextSize(), 0f);
+
+ // Check the correct part of the text is highlighted.
+ final int expectedStart;
+ final int expectedEnd;
+ if (j < singleWordCandidates.length) {
+ expectedStart = text.indexOf('d');
+ expectedEnd = text.indexOf('f') + 1;
+ } else {
+ expectedStart = 0;
+ expectedEnd = text.length();
+ }
+ assertEquals(expectedStart, spanned.getSpanStart(taSpan[0]));
+ assertEquals(expectedEnd, spanned.getSpanEnd(taSpan[0]));
}
});
pressBack();
diff --git a/core/tests/coretests/src/android/widget/TextViewActivityMouseTest.java b/core/tests/coretests/src/android/widget/TextViewActivityMouseTest.java
index 41fa08b..b411668 100644
--- a/core/tests/coretests/src/android/widget/TextViewActivityMouseTest.java
+++ b/core/tests/coretests/src/android/widget/TextViewActivityMouseTest.java
@@ -16,15 +16,6 @@
package android.widget;
-
-import static android.support.test.espresso.Espresso.onView;
-import static android.support.test.espresso.Espresso.pressBack;
-import static android.support.test.espresso.action.ViewActions.replaceText;
-import static android.support.test.espresso.action.ViewActions.typeText;
-import static android.support.test.espresso.assertion.ViewAssertions.matches;
-import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
-import static android.support.test.espresso.matcher.ViewMatchers.withId;
-import static android.support.test.espresso.matcher.ViewMatchers.withText;
import static android.widget.espresso.ContextMenuUtils.assertContextMenuContainsItemDisabled;
import static android.widget.espresso.ContextMenuUtils.assertContextMenuContainsItemEnabled;
import static android.widget.espresso.ContextMenuUtils.assertContextMenuIsNotDisplayed;
@@ -42,6 +33,15 @@
import static android.widget.espresso.TextViewAssertions.hasInsertionPointerAtIndex;
import static android.widget.espresso.TextViewAssertions.hasSelection;
+import static androidx.test.espresso.Espresso.onView;
+import static androidx.test.espresso.Espresso.pressBack;
+import static androidx.test.espresso.action.ViewActions.replaceText;
+import static androidx.test.espresso.action.ViewActions.typeText;
+import static androidx.test.espresso.assertion.ViewAssertions.matches;
+import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static androidx.test.espresso.matcher.ViewMatchers.withId;
+import static androidx.test.espresso.matcher.ViewMatchers.withText;
+
import android.app.Activity;
import android.view.MotionEvent;
import android.view.textclassifier.TextClassificationManager;
diff --git a/core/tests/coretests/src/android/widget/TextViewActivityTest.java b/core/tests/coretests/src/android/widget/TextViewActivityTest.java
index 9d93421..267a9c8 100644
--- a/core/tests/coretests/src/android/widget/TextViewActivityTest.java
+++ b/core/tests/coretests/src/android/widget/TextViewActivityTest.java
@@ -16,15 +16,6 @@
package android.widget;
-import static android.support.test.espresso.Espresso.onView;
-import static android.support.test.espresso.action.ViewActions.click;
-import static android.support.test.espresso.action.ViewActions.longClick;
-import static android.support.test.espresso.action.ViewActions.pressKey;
-import static android.support.test.espresso.action.ViewActions.replaceText;
-import static android.support.test.espresso.assertion.ViewAssertions.matches;
-import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
-import static android.support.test.espresso.matcher.ViewMatchers.withId;
-import static android.support.test.espresso.matcher.ViewMatchers.withText;
import static android.widget.espresso.CustomViewActions.longPressAtRelativeCoordinates;
import static android.widget.espresso.DragHandleUtils.onHandleView;
import static android.widget.espresso.FloatingToolbarEspressoUtils.assertFloatingToolbarContainsItem;
@@ -44,6 +35,16 @@
import static android.widget.espresso.TextViewAssertions.hasInsertionPointerAtIndex;
import static android.widget.espresso.TextViewAssertions.hasSelection;
+import static androidx.test.espresso.Espresso.onView;
+import static androidx.test.espresso.action.ViewActions.click;
+import static androidx.test.espresso.action.ViewActions.longClick;
+import static androidx.test.espresso.action.ViewActions.pressKey;
+import static androidx.test.espresso.action.ViewActions.replaceText;
+import static androidx.test.espresso.assertion.ViewAssertions.matches;
+import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static androidx.test.espresso.matcher.ViewMatchers.withId;
+import static androidx.test.espresso.matcher.ViewMatchers.withText;
+
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
@@ -60,7 +61,6 @@
import android.app.Instrumentation;
import android.content.ClipData;
import android.content.ClipboardManager;
-import android.support.test.espresso.action.EspressoKey;
import android.support.test.uiautomator.UiDevice;
import android.text.InputType;
import android.text.Selection;
@@ -79,6 +79,7 @@
import android.widget.espresso.CustomViewActions.RelativeCoordinatesProvider;
import androidx.test.InstrumentationRegistry;
+import androidx.test.espresso.action.EspressoKey;
import androidx.test.filters.MediumTest;
import androidx.test.filters.Suppress;
import androidx.test.rule.ActivityTestRule;
diff --git a/core/tests/coretests/src/android/widget/espresso/ContextMenuUtils.java b/core/tests/coretests/src/android/widget/espresso/ContextMenuUtils.java
index 02ee9be..0f8faa8 100644
--- a/core/tests/coretests/src/android/widget/espresso/ContextMenuUtils.java
+++ b/core/tests/coretests/src/android/widget/espresso/ContextMenuUtils.java
@@ -16,28 +16,29 @@
package android.widget.espresso;
-import static android.support.test.espresso.Espresso.onView;
-import static android.support.test.espresso.action.ViewActions.click;
-import static android.support.test.espresso.assertion.ViewAssertions.matches;
-import static android.support.test.espresso.matcher.RootMatchers.withDecorView;
-import static android.support.test.espresso.matcher.ViewMatchers.hasDescendant;
-import static android.support.test.espresso.matcher.ViewMatchers.hasFocus;
-import static android.support.test.espresso.matcher.ViewMatchers.isAssignableFrom;
-import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
-import static android.support.test.espresso.matcher.ViewMatchers.isDisplayingAtLeast;
-import static android.support.test.espresso.matcher.ViewMatchers.isEnabled;
-import static android.support.test.espresso.matcher.ViewMatchers.withText;
+import static androidx.test.espresso.Espresso.onView;
+import static androidx.test.espresso.action.ViewActions.click;
+import static androidx.test.espresso.assertion.ViewAssertions.matches;
+import static androidx.test.espresso.matcher.RootMatchers.withDecorView;
+import static androidx.test.espresso.matcher.ViewMatchers.hasDescendant;
+import static androidx.test.espresso.matcher.ViewMatchers.hasFocus;
+import static androidx.test.espresso.matcher.ViewMatchers.isAssignableFrom;
+import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static androidx.test.espresso.matcher.ViewMatchers.isDisplayingAtLeast;
+import static androidx.test.espresso.matcher.ViewMatchers.isEnabled;
+import static androidx.test.espresso.matcher.ViewMatchers.withText;
import static org.hamcrest.Matchers.allOf;
import static org.hamcrest.Matchers.not;
-import android.support.test.espresso.NoMatchingRootException;
-import android.support.test.espresso.NoMatchingViewException;
-import android.support.test.espresso.ViewInteraction;
-import android.support.test.espresso.matcher.ViewMatchers;
import android.view.View;
import android.widget.MenuPopupWindow.MenuDropDownListView;
+import androidx.test.espresso.NoMatchingRootException;
+import androidx.test.espresso.NoMatchingViewException;
+import androidx.test.espresso.ViewInteraction;
+import androidx.test.espresso.matcher.ViewMatchers;
+
import com.android.internal.view.menu.ListMenuItemView;
import org.hamcrest.Description;
diff --git a/core/tests/coretests/src/android/widget/espresso/CustomViewActions.java b/core/tests/coretests/src/android/widget/espresso/CustomViewActions.java
index daf9e78..217553e 100644
--- a/core/tests/coretests/src/android/widget/espresso/CustomViewActions.java
+++ b/core/tests/coretests/src/android/widget/espresso/CustomViewActions.java
@@ -16,15 +16,15 @@
package android.widget.espresso;
-import static android.support.test.espresso.action.ViewActions.actionWithAssertions;
+import static androidx.test.espresso.action.ViewActions.actionWithAssertions;
import android.view.View;
-import android.support.test.espresso.ViewAction;
-import android.support.test.espresso.action.CoordinatesProvider;
-import android.support.test.espresso.action.GeneralClickAction;
-import android.support.test.espresso.action.Press;
-import android.support.test.espresso.action.Tap;
+import androidx.test.espresso.ViewAction;
+import androidx.test.espresso.action.CoordinatesProvider;
+import androidx.test.espresso.action.GeneralClickAction;
+import androidx.test.espresso.action.Press;
+import androidx.test.espresso.action.Tap;
import com.android.internal.util.Preconditions;
diff --git a/core/tests/coretests/src/android/widget/espresso/DragAction.java b/core/tests/coretests/src/android/widget/espresso/DragAction.java
index b2c8e38..afdc4d3 100644
--- a/core/tests/coretests/src/android/widget/espresso/DragAction.java
+++ b/core/tests/coretests/src/android/widget/espresso/DragAction.java
@@ -16,27 +16,30 @@
package android.widget.espresso;
-import static android.support.test.espresso.matcher.ViewMatchers.isAssignableFrom;
-import static android.support.test.espresso.matcher.ViewMatchers.isCompletelyDisplayed;
+import static androidx.test.espresso.matcher.ViewMatchers.isAssignableFrom;
+import static androidx.test.espresso.matcher.ViewMatchers.isCompletelyDisplayed;
+
import static com.android.internal.util.Preconditions.checkNotNull;
+
import static org.hamcrest.Matchers.allOf;
+
import android.annotation.Nullable;
import android.os.SystemClock;
-import android.support.test.espresso.UiController;
-import android.support.test.espresso.PerformException;
-import android.support.test.espresso.ViewAction;
-import android.support.test.espresso.action.CoordinatesProvider;
-import android.support.test.espresso.action.MotionEvents;
-import android.support.test.espresso.action.PrecisionDescriber;
-import android.support.test.espresso.action.Swiper;
-import android.support.test.espresso.util.HumanReadables;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
-import org.hamcrest.Matcher;
+import androidx.test.espresso.PerformException;
+import androidx.test.espresso.UiController;
+import androidx.test.espresso.ViewAction;
+import androidx.test.espresso.action.CoordinatesProvider;
+import androidx.test.espresso.action.MotionEvents;
+import androidx.test.espresso.action.PrecisionDescriber;
+import androidx.test.espresso.action.Swiper;
+import androidx.test.espresso.util.HumanReadables;
+import org.hamcrest.Matcher;
/**
* Drags on a View using touch events.<br>
diff --git a/core/tests/coretests/src/android/widget/espresso/DragHandleUtils.java b/core/tests/coretests/src/android/widget/espresso/DragHandleUtils.java
index 1693e54..1b849b5 100644
--- a/core/tests/coretests/src/android/widget/espresso/DragHandleUtils.java
+++ b/core/tests/coretests/src/android/widget/espresso/DragHandleUtils.java
@@ -16,22 +16,23 @@
package android.widget.espresso;
-import static android.support.test.espresso.Espresso.onView;
-import static android.support.test.espresso.assertion.ViewAssertions.matches;
-import static android.support.test.espresso.matcher.RootMatchers.isPlatformPopup;
-import static android.support.test.espresso.matcher.RootMatchers.withDecorView;
-import static android.support.test.espresso.matcher.ViewMatchers.hasDescendant;
-import static android.support.test.espresso.matcher.ViewMatchers.isAssignableFrom;
-import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
-import static android.support.test.espresso.matcher.ViewMatchers.withId;
+import static androidx.test.espresso.Espresso.onView;
+import static androidx.test.espresso.assertion.ViewAssertions.matches;
+import static androidx.test.espresso.matcher.RootMatchers.isPlatformPopup;
+import static androidx.test.espresso.matcher.RootMatchers.withDecorView;
+import static androidx.test.espresso.matcher.ViewMatchers.hasDescendant;
+import static androidx.test.espresso.matcher.ViewMatchers.isAssignableFrom;
+import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static androidx.test.espresso.matcher.ViewMatchers.withId;
import static org.hamcrest.Matchers.allOf;
-import android.support.test.espresso.NoMatchingRootException;
-import android.support.test.espresso.NoMatchingViewException;
-import android.support.test.espresso.ViewInteraction;
import android.widget.Editor;
+import androidx.test.espresso.NoMatchingRootException;
+import androidx.test.espresso.NoMatchingViewException;
+import androidx.test.espresso.ViewInteraction;
+
public final class DragHandleUtils {
private DragHandleUtils() {}
diff --git a/core/tests/coretests/src/android/widget/espresso/FloatingToolbarEspressoUtils.java b/core/tests/coretests/src/android/widget/espresso/FloatingToolbarEspressoUtils.java
index 0355f82..d45d4b0 100644
--- a/core/tests/coretests/src/android/widget/espresso/FloatingToolbarEspressoUtils.java
+++ b/core/tests/coretests/src/android/widget/espresso/FloatingToolbarEspressoUtils.java
@@ -16,30 +16,31 @@
package android.widget.espresso;
-import static android.support.test.espresso.Espresso.onView;
-import static android.support.test.espresso.action.ViewActions.click;
-import static android.support.test.espresso.assertion.ViewAssertions.matches;
-import static android.support.test.espresso.matcher.RootMatchers.isPlatformPopup;
-import static android.support.test.espresso.matcher.RootMatchers.withDecorView;
-import static android.support.test.espresso.matcher.ViewMatchers.hasDescendant;
-import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
-import static android.support.test.espresso.matcher.ViewMatchers.isRoot;
-import static android.support.test.espresso.matcher.ViewMatchers.withId;
-import static android.support.test.espresso.matcher.ViewMatchers.withTagValue;
-import static android.support.test.espresso.matcher.ViewMatchers.withText;
+import static androidx.test.espresso.Espresso.onView;
+import static androidx.test.espresso.action.ViewActions.click;
+import static androidx.test.espresso.assertion.ViewAssertions.matches;
+import static androidx.test.espresso.matcher.RootMatchers.isPlatformPopup;
+import static androidx.test.espresso.matcher.RootMatchers.withDecorView;
+import static androidx.test.espresso.matcher.ViewMatchers.hasDescendant;
+import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static androidx.test.espresso.matcher.ViewMatchers.isRoot;
+import static androidx.test.espresso.matcher.ViewMatchers.withId;
+import static androidx.test.espresso.matcher.ViewMatchers.withTagValue;
+import static androidx.test.espresso.matcher.ViewMatchers.withText;
import static org.hamcrest.Matchers.allOf;
import static org.hamcrest.Matchers.is;
-import android.support.test.espresso.NoMatchingRootException;
-import android.support.test.espresso.NoMatchingViewException;
-import android.support.test.espresso.UiController;
-import android.support.test.espresso.ViewAction;
-import android.support.test.espresso.ViewInteraction;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
+import androidx.test.espresso.NoMatchingRootException;
+import androidx.test.espresso.NoMatchingViewException;
+import androidx.test.espresso.UiController;
+import androidx.test.espresso.ViewAction;
+import androidx.test.espresso.ViewInteraction;
+
import com.android.internal.widget.FloatingToolbar;
import org.hamcrest.Description;
diff --git a/core/tests/coretests/src/android/widget/espresso/MouseClickAction.java b/core/tests/coretests/src/android/widget/espresso/MouseClickAction.java
index b50d6f4..f56af5a 100644
--- a/core/tests/coretests/src/android/widget/espresso/MouseClickAction.java
+++ b/core/tests/coretests/src/android/widget/espresso/MouseClickAction.java
@@ -16,18 +16,19 @@
package android.widget.espresso;
-import android.support.test.espresso.UiController;
-import android.support.test.espresso.ViewAction;
-import android.support.test.espresso.action.CoordinatesProvider;
-import android.support.test.espresso.action.MotionEvents;
-import android.support.test.espresso.action.MotionEvents.DownResultHolder;
-import android.support.test.espresso.action.Press;
-import android.support.test.espresso.action.Tapper;
import android.view.InputDevice;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
+import androidx.test.espresso.UiController;
+import androidx.test.espresso.ViewAction;
+import androidx.test.espresso.action.CoordinatesProvider;
+import androidx.test.espresso.action.MotionEvents;
+import androidx.test.espresso.action.MotionEvents.DownResultHolder;
+import androidx.test.espresso.action.Press;
+import androidx.test.espresso.action.Tapper;
+
import org.hamcrest.Matcher;
/**
diff --git a/core/tests/coretests/src/android/widget/espresso/MouseUiController.java b/core/tests/coretests/src/android/widget/espresso/MouseUiController.java
index 022be76..abee736 100644
--- a/core/tests/coretests/src/android/widget/espresso/MouseUiController.java
+++ b/core/tests/coretests/src/android/widget/espresso/MouseUiController.java
@@ -18,16 +18,19 @@
import static com.android.internal.util.Preconditions.checkNotNull;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
import android.annotation.IntDef;
-import android.support.test.espresso.InjectEventSecurityException;
-import android.support.test.espresso.UiController;
+import android.os.SystemClock;
import android.view.InputDevice;
import android.view.KeyEvent;
import android.view.MotionEvent;
+import androidx.test.espresso.InjectEventSecurityException;
+import androidx.test.espresso.UiController;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Iterator;
+
/**
* Class to wrap an UiController to overwrite source of motion events to SOURCE_MOUSE.
* Note that this doesn't change the tool type.
@@ -71,6 +74,32 @@
return mUiController.injectMotionEvent(event);
}
+ /**
+ * Copied from latest {@link androidx.test.espresso.UiController}, since current
+ * {@link androidx.test.espresso.UiController#injectMotionEventSequence(Iterable)} seems not a
+ * default method.
+ */
+ @Override
+ public boolean injectMotionEventSequence(Iterable<MotionEvent> events)
+ throws InjectEventSecurityException {
+ android.util.Log.w(
+ "UIC",
+ "Using default injectMotionEventSeq() - this may not inject a sequence properly. "
+ + "If wrapping UIController please override this method and delegate.");
+ Iterator<MotionEvent> mei = events.iterator();
+ boolean success = true;
+ while (mei.hasNext()) {
+ MotionEvent me = mei.next();
+ if (me.getEventTime() - SystemClock.uptimeMillis() > 10) {
+ // Because the loopMainThreadForAtLeast is overkill for waiting, intentially only
+ // call it with a smaller amount of milliseconds as best effort
+ loopMainThreadForAtLeast(10);
+ }
+ success &= injectMotionEvent(me);
+ }
+ return success;
+ }
+
@Override
public boolean injectString(String str) throws InjectEventSecurityException {
return mUiController.injectString(str);
diff --git a/core/tests/coretests/src/android/widget/espresso/SuggestionsPopupwindowUtils.java b/core/tests/coretests/src/android/widget/espresso/SuggestionsPopupwindowUtils.java
index b5a96ae..32c02403 100644
--- a/core/tests/coretests/src/android/widget/espresso/SuggestionsPopupwindowUtils.java
+++ b/core/tests/coretests/src/android/widget/espresso/SuggestionsPopupwindowUtils.java
@@ -16,36 +16,40 @@
package android.widget.espresso;
-import static android.support.test.espresso.Espresso.onView;
-import static android.support.test.espresso.assertion.ViewAssertions.matches;
-import static android.support.test.espresso.matcher.RootMatchers.withDecorView;
-import static android.support.test.espresso.matcher.ViewMatchers.hasDescendant;
-import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
-import static android.support.test.espresso.matcher.ViewMatchers.withId;
-import static android.support.test.espresso.matcher.ViewMatchers.withText;
+import static androidx.test.espresso.Espresso.onView;
+import static androidx.test.espresso.assertion.ViewAssertions.matches;
+import static androidx.test.espresso.matcher.RootMatchers.withDecorView;
+import static androidx.test.espresso.matcher.ViewMatchers.hasDescendant;
+import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static androidx.test.espresso.matcher.ViewMatchers.withId;
+import static androidx.test.espresso.matcher.ViewMatchers.withText;
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import android.view.View;
+
+import androidx.test.espresso.NoMatchingRootException;
+import androidx.test.espresso.NoMatchingViewException;
+import androidx.test.espresso.UiController;
+import androidx.test.espresso.ViewAction;
+import androidx.test.espresso.ViewInteraction;
+import androidx.test.espresso.action.GeneralLocation;
+import androidx.test.espresso.action.Press;
+import androidx.test.espresso.action.Tap;
import org.hamcrest.Matcher;
-import android.support.test.espresso.NoMatchingRootException;
-import android.support.test.espresso.NoMatchingViewException;
-import android.support.test.espresso.UiController;
-import android.support.test.espresso.ViewAction;
-import android.support.test.espresso.ViewInteraction;
-import android.support.test.espresso.action.GeneralLocation;
-import android.support.test.espresso.action.Press;
-import android.support.test.espresso.action.Tap;
-import android.view.View;
-
public final class SuggestionsPopupwindowUtils {
private static final int id = com.android.internal.R.id.suggestionWindowContainer;
private SuggestionsPopupwindowUtils() {};
public static ViewInteraction onSuggestionsPopup() {
+ getInstrumentation().waitForIdleSync();
return onView(withId(id)).inRoot(withDecorView(hasDescendant(withId(id))));
}
private static ViewInteraction onSuggestionsPopupItem(Matcher<View> matcher) {
+ getInstrumentation().waitForIdleSync();
return onView(matcher).inRoot(withDecorView(hasDescendant(withId(id))));
}
diff --git a/core/tests/coretests/src/android/widget/espresso/TextViewActions.java b/core/tests/coretests/src/android/widget/espresso/TextViewActions.java
index b4c547e..83ce67b 100644
--- a/core/tests/coretests/src/android/widget/espresso/TextViewActions.java
+++ b/core/tests/coretests/src/android/widget/espresso/TextViewActions.java
@@ -16,16 +16,9 @@
package android.widget.espresso;
-import static android.support.test.espresso.action.ViewActions.actionWithAssertions;
+import static androidx.test.espresso.action.ViewActions.actionWithAssertions;
import android.graphics.Rect;
-import android.support.test.espresso.PerformException;
-import android.support.test.espresso.ViewAction;
-import android.support.test.espresso.action.CoordinatesProvider;
-import android.support.test.espresso.action.GeneralLocation;
-import android.support.test.espresso.action.Press;
-import android.support.test.espresso.action.Tap;
-import android.support.test.espresso.util.HumanReadables;
import android.text.Layout;
import android.view.MotionEvent;
import android.view.View;
@@ -33,6 +26,14 @@
import android.widget.Editor.HandleView;
import android.widget.TextView;
+import androidx.test.espresso.PerformException;
+import androidx.test.espresso.ViewAction;
+import androidx.test.espresso.action.CoordinatesProvider;
+import androidx.test.espresso.action.GeneralLocation;
+import androidx.test.espresso.action.Press;
+import androidx.test.espresso.action.Tap;
+import androidx.test.espresso.util.HumanReadables;
+
/**
* A collection of actions on a {@link android.widget.TextView}.
*/
diff --git a/core/tests/coretests/src/android/widget/espresso/TextViewAssertions.java b/core/tests/coretests/src/android/widget/espresso/TextViewAssertions.java
index 5ed69e0..d638da5 100644
--- a/core/tests/coretests/src/android/widget/espresso/TextViewAssertions.java
+++ b/core/tests/coretests/src/android/widget/espresso/TextViewAssertions.java
@@ -16,7 +16,7 @@
package android.widget.espresso;
-import static android.support.test.espresso.matcher.ViewMatchers.assertThat;
+import static androidx.test.espresso.matcher.ViewMatchers.assertThat;
import static com.android.internal.util.Preconditions.checkNotNull;
@@ -26,14 +26,15 @@
import android.annotation.IntDef;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
-import android.support.test.espresso.NoMatchingViewException;
-import android.support.test.espresso.ViewAssertion;
import android.text.Spanned;
import android.text.TextUtils;
import android.view.View;
import android.widget.EditText;
import android.widget.TextView;
+import androidx.test.espresso.NoMatchingViewException;
+import androidx.test.espresso.ViewAssertion;
+
import junit.framework.AssertionFailedError;
import org.hamcrest.Matcher;
diff --git a/core/tests/coretests/src/android/widget/espresso/ViewClickAction.java b/core/tests/coretests/src/android/widget/espresso/ViewClickAction.java
index 8bce1b0..1e6447d 100644
--- a/core/tests/coretests/src/android/widget/espresso/ViewClickAction.java
+++ b/core/tests/coretests/src/android/widget/espresso/ViewClickAction.java
@@ -16,17 +16,18 @@
package android.widget.espresso;
-import org.hamcrest.Matcher;
-
-import android.support.test.espresso.UiController;
-import android.support.test.espresso.ViewAction;
-import android.support.test.espresso.action.CoordinatesProvider;
-import android.support.test.espresso.action.GeneralClickAction;
-import android.support.test.espresso.action.PrecisionDescriber;
-import android.support.test.espresso.action.Tapper;
import android.view.View;
import android.view.ViewConfiguration;
+import androidx.test.espresso.UiController;
+import androidx.test.espresso.ViewAction;
+import androidx.test.espresso.action.CoordinatesProvider;
+import androidx.test.espresso.action.GeneralClickAction;
+import androidx.test.espresso.action.PrecisionDescriber;
+import androidx.test.espresso.action.Tapper;
+
+import org.hamcrest.Matcher;
+
public final class ViewClickAction implements ViewAction {
private final GeneralClickAction mGeneralClickAction;
diff --git a/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
index fe2fb85..7430c7a 100644
--- a/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
@@ -16,12 +16,12 @@
package com.android.internal.app;
-import static android.support.test.espresso.Espresso.onView;
-import static android.support.test.espresso.action.ViewActions.click;
-import static android.support.test.espresso.assertion.ViewAssertions.matches;
-import static android.support.test.espresso.matcher.ViewMatchers.isEnabled;
-import static android.support.test.espresso.matcher.ViewMatchers.withId;
-import static android.support.test.espresso.matcher.ViewMatchers.withText;
+import static androidx.test.espresso.Espresso.onView;
+import static androidx.test.espresso.action.ViewActions.click;
+import static androidx.test.espresso.assertion.ViewAssertions.matches;
+import static androidx.test.espresso.matcher.ViewMatchers.isEnabled;
+import static androidx.test.espresso.matcher.ViewMatchers.withId;
+import static androidx.test.espresso.matcher.ViewMatchers.withText;
import static com.android.internal.app.ResolverWrapperActivity.sOverrides;
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderEndToEndTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderEndToEndTest.java
index 3ddd8aa..64b7c2c 100644
--- a/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderEndToEndTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuThreadReaderEndToEndTest.java
@@ -22,8 +22,9 @@
import android.os.Process;
import android.os.SystemClock;
-import android.support.test.filters.LargeTest;
-import android.support.test.runner.AndroidJUnit4;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.LargeTest;
import com.android.internal.os.KernelCpuThreadReader.ProcessCpuUsage;
import com.android.internal.os.KernelCpuThreadReader.ThreadCpuUsage;
diff --git a/core/tests/coretests/src/com/android/internal/statusbar/NotificationVisibilityTest.java b/core/tests/coretests/src/com/android/internal/statusbar/NotificationVisibilityTest.java
index b740ecc..22432a8 100644
--- a/core/tests/coretests/src/com/android/internal/statusbar/NotificationVisibilityTest.java
+++ b/core/tests/coretests/src/com/android/internal/statusbar/NotificationVisibilityTest.java
@@ -19,8 +19,8 @@
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
diff --git a/data/etc/Android.bp b/data/etc/Android.bp
index 035ee10..bb47658 100644
--- a/data/etc/Android.bp
+++ b/data/etc/Android.bp
@@ -58,6 +58,14 @@
}
prebuilt_etc {
+ name: "privapp_whitelist_com.android.dialer",
+ product_specific: true,
+ sub_dir: "permissions",
+ src: "com.android.dialer.xml",
+ filename_from_src: true,
+}
+
+prebuilt_etc {
name: "privapp_whitelist_com.android.launcher3",
product_specific: true,
sub_dir: "permissions",
diff --git a/data/etc/com.android.dialer.xml b/data/etc/com.android.dialer.xml
new file mode 100644
index 0000000..ccdb21f
--- /dev/null
+++ b/data/etc/com.android.dialer.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<permissions>
+ <privapp-permissions package="com.android.dialer">
+ <permission name="android.permission.ALLOW_ANY_CODEC_FOR_PLAYBACK"/>
+ <permission name="android.permission.CONTROL_INCALL_EXPERIENCE"/>
+ <permission name="android.permission.GET_ACCOUNTS_PRIVILEGED"/>
+ <permission name="android.permission.MODIFY_PHONE_STATE"/>
+ <permission name="android.permission.STATUS_BAR"/>
+ <permission name="android.permission.STOP_APP_SWITCHES"/>
+ <permission name="com.android.voicemail.permission.READ_VOICEMAIL"/>
+ <permission name="com.android.voicemail.permission.WRITE_VOICEMAIL"/>
+ </privapp-permissions>
+</permissions>
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 2e7b998..4ef5adb 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -49,17 +49,6 @@
<permission name="android.permission.WRITE_MEDIA_STORAGE"/>
</privapp-permissions>
- <privapp-permissions package="com.android.dialer">
- <permission name="android.permission.ALLOW_ANY_CODEC_FOR_PLAYBACK"/>
- <permission name="android.permission.CONTROL_INCALL_EXPERIENCE"/>
- <permission name="android.permission.GET_ACCOUNTS_PRIVILEGED"/>
- <permission name="android.permission.MODIFY_PHONE_STATE"/>
- <permission name="android.permission.STATUS_BAR"/>
- <permission name="android.permission.STOP_APP_SWITCHES"/>
- <permission name="com.android.voicemail.permission.READ_VOICEMAIL"/>
- <permission name="com.android.voicemail.permission.WRITE_VOICEMAIL"/>
- </privapp-permissions>
-
<privapp-permissions package="com.android.emergency">
<!-- Required to place emergency calls from emergency info screen. -->
<permission name="android.permission.CALL_PRIVILEGED"/>
diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
index b67aea2..d945635 100644
--- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
@@ -142,10 +142,8 @@
void SkiaRecordingCanvas::drawWebViewFunctor(int functor) {
FunctorDrawable* functorDrawable;
if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) {
- // TODO(cblume) use VkFunctorDrawable instead of VkInteropFunctorDrawable here when the
- // interop is disabled.
functorDrawable =
- mDisplayList->allocateDrawable<VkInteropFunctorDrawable>(functor, asSkCanvas());
+ mDisplayList->allocateDrawable<VkFunctorDrawable>(functor, asSkCanvas());
} else {
functorDrawable = mDisplayList->allocateDrawable<GLFunctorDrawable>(functor, asSkCanvas());
}
diff --git a/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp b/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp
index 2f8d381..fe2d41e 100644
--- a/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp
+++ b/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp
@@ -43,7 +43,9 @@
, mImageInfo(image_info) {}
VkFunctorDrawHandler::~VkFunctorDrawHandler() {
- mFunctorHandle->postDrawVk();
+ if (mDrawn) {
+ mFunctorHandle->postDrawVk();
+ }
}
void VkFunctorDrawHandler::draw(const GrBackendDrawableInfo& info) {
@@ -77,6 +79,7 @@
params.format = vulkan_info.fFormat;
mFunctorHandle->drawVk(params);
+ mDrawn = true;
vulkan_info.fDrawBounds->offset.x = mClip.fLeft;
vulkan_info.fDrawBounds->offset.y = mClip.fTop;
diff --git a/libs/hwui/pipeline/skia/VkFunctorDrawable.h b/libs/hwui/pipeline/skia/VkFunctorDrawable.h
index 1a53c8f..d3f9777 100644
--- a/libs/hwui/pipeline/skia/VkFunctorDrawable.h
+++ b/libs/hwui/pipeline/skia/VkFunctorDrawable.h
@@ -44,6 +44,8 @@
const SkMatrix mMatrix;
const SkIRect mClip;
const SkImageInfo mImageInfo;
+
+ bool mDrawn = false;
};
/**
diff --git a/native/webview/plat_support/draw_functor.cpp b/native/webview/plat_support/draw_functor.cpp
index 6deb47f..e43a60c 100644
--- a/native/webview/plat_support/draw_functor.cpp
+++ b/native/webview/plat_support/draw_functor.cpp
@@ -177,9 +177,6 @@
webview_functor_callbacks.vk.initialize = &initializeVk;
webview_functor_callbacks.vk.draw = &drawVk;
webview_functor_callbacks.vk.postDraw = &postDrawVk;
- // TODO(boliu): Remove this once SkiaRecordingCanvas::drawWebViewFunctor
- // no longer uses GL interop.
- webview_functor_callbacks.gles.draw = &draw_gl;
break;
}
callbacks_initialized = true;
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 815ae9a..866b46f 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -631,6 +631,14 @@
android:exported="true">
</provider>
+ <!-- Provides list and realistic previews of clock faces for the picker app. -->
+ <provider
+ android:name="com.android.keyguard.clock.ClockOptionsProvider"
+ android:authorities="com.android.keyguard.clock"
+ android:exported="true"
+ android:grantUriPermissions="true">
+ </provider>
+
<receiver
android:name=".statusbar.KeyboardShortcutsReceiver">
<intent-filter>
diff --git a/packages/SystemUI/res-keyguard/drawable-xxxhdpi/bubble_preview.png b/packages/SystemUI/res-keyguard/drawable-xxxhdpi/bubble_preview.png
new file mode 100644
index 0000000..67f072f
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/drawable-xxxhdpi/bubble_preview.png
Binary files differ
diff --git a/packages/SystemUI/res-keyguard/drawable-xxxhdpi/bubble_thumbnail.png b/packages/SystemUI/res-keyguard/drawable-xxxhdpi/bubble_thumbnail.png
new file mode 100644
index 0000000..8d0e6ed
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/drawable-xxxhdpi/bubble_thumbnail.png
Binary files differ
diff --git a/packages/SystemUI/res-keyguard/drawable-xxxhdpi/default_preview.png b/packages/SystemUI/res-keyguard/drawable-xxxhdpi/default_preview.png
new file mode 100644
index 0000000..035a4df
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/drawable-xxxhdpi/default_preview.png
Binary files differ
diff --git a/packages/SystemUI/res-keyguard/drawable-xxxhdpi/default_thumbnail.png b/packages/SystemUI/res-keyguard/drawable-xxxhdpi/default_thumbnail.png
new file mode 100644
index 0000000..1ac0113
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/drawable-xxxhdpi/default_thumbnail.png
Binary files differ
diff --git a/packages/SystemUI/res-keyguard/drawable-xxxhdpi/stretch_preview.png b/packages/SystemUI/res-keyguard/drawable-xxxhdpi/stretch_preview.png
new file mode 100644
index 0000000..63927bc
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/drawable-xxxhdpi/stretch_preview.png
Binary files differ
diff --git a/packages/SystemUI/res-keyguard/drawable-xxxhdpi/stretch_thumbnail.png b/packages/SystemUI/res-keyguard/drawable-xxxhdpi/stretch_thumbnail.png
new file mode 100644
index 0000000..83d714b
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/drawable-xxxhdpi/stretch_thumbnail.png
Binary files differ
diff --git a/packages/SystemUI/res-keyguard/drawable-xxxhdpi/type_preview.png b/packages/SystemUI/res-keyguard/drawable-xxxhdpi/type_preview.png
new file mode 100644
index 0000000..a538149
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/drawable-xxxhdpi/type_preview.png
Binary files differ
diff --git a/packages/SystemUI/res-keyguard/drawable-xxxhdpi/type_thumbnail.png b/packages/SystemUI/res-keyguard/drawable-xxxhdpi/type_thumbnail.png
new file mode 100644
index 0000000..2bfd655
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/drawable-xxxhdpi/type_thumbnail.png
Binary files differ
diff --git a/packages/SystemUI/res-keyguard/values/strings.xml b/packages/SystemUI/res-keyguard/values/strings.xml
index 41acf82..1f33307 100644
--- a/packages/SystemUI/res-keyguard/values/strings.xml
+++ b/packages/SystemUI/res-keyguard/values/strings.xml
@@ -485,4 +485,17 @@
<item>Fifty\nNine</item>
</string-array>
+ <!-- Title for default clock face that will appear in the picker app next to a preview image of
+ the clock face. [CHAR LIMIT=8] -->
+ <string name="clock_title_default" translatable="false">Default</string>
+ <!-- Title for Bubble clock face that will appear in the picker app next to a preview image of
+ the clock face. [CHAR LIMIT=8] -->
+ <string name="clock_title_bubble" translatable="false">Bubble</string>
+ <!-- Title for Stretch clock face that will appear in the picker app next to a preview image of
+ the clock face. [CHAR LIMIT=8] -->
+ <string name="clock_title_stretch" translatable="false">Stretch</string>
+ <!-- Title for Typographic clock face that will appear in the picker app next to a preview image of
+ the clock face. [CHAR LIMIT=8] -->
+ <string name="clock_title_type" translatable="false">Type</string>
+
</resources>
diff --git a/packages/SystemUI/src/com/android/keyguard/clock/ClockInfo.java b/packages/SystemUI/src/com/android/keyguard/clock/ClockInfo.java
new file mode 100644
index 0000000..812f215
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/clock/ClockInfo.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.keyguard.clock;
+
+import android.graphics.Bitmap;
+
+import java.util.function.Supplier;
+
+/**
+ * Metadata about an available clock face.
+ */
+final class ClockInfo {
+
+ private final String mName;
+ private final String mTitle;
+ private final String mId;
+ private final Supplier<Bitmap> mThumbnail;
+ private final Supplier<Bitmap> mPreview;
+
+ private ClockInfo(String name, String title, String id,
+ Supplier<Bitmap> thumbnail, Supplier<Bitmap> preview) {
+ mName = name;
+ mTitle = title;
+ mId = id;
+ mThumbnail = thumbnail;
+ mPreview = preview;
+ }
+
+ /**
+ * Gets the non-internationalized name for the clock face.
+ */
+ String getName() {
+ return mName;
+ }
+
+ /**
+ * Gets the name (title) of the clock face to be shown in the picker app.
+ */
+ String getTitle() {
+ return mTitle;
+ }
+
+ /**
+ * Gets the ID of the clock face, used by the picker to set the current selection.
+ */
+ String getId() {
+ return mId;
+ }
+
+ /**
+ * Gets a thumbnail image of the clock.
+ */
+ Bitmap getThumbnail() {
+ return mThumbnail.get();
+ }
+
+ /**
+ * Gets a potentially realistic preview image of the clock face.
+ */
+ Bitmap getPreview() {
+ return mPreview.get();
+ }
+
+ static Builder builder() {
+ return new Builder();
+ }
+
+ static class Builder {
+ private String mName;
+ private String mTitle;
+ private String mId;
+ private Supplier<Bitmap> mThumbnail;
+ private Supplier<Bitmap> mPreview;
+
+ public ClockInfo build() {
+ return new ClockInfo(mName, mTitle, mId, mThumbnail, mPreview);
+ }
+
+ public Builder setName(String name) {
+ mName = name;
+ return this;
+ }
+
+ public Builder setTitle(String title) {
+ mTitle = title;
+ return this;
+ }
+
+ public Builder setId(String id) {
+ mId = id;
+ return this;
+ }
+
+ public Builder setThumbnail(Supplier<Bitmap> thumbnail) {
+ mThumbnail = thumbnail;
+ return this;
+ }
+
+ public Builder setPreview(Supplier<Bitmap> preview) {
+ mPreview = preview;
+ return this;
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java b/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java
index 3217ca6..9598142 100644
--- a/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java
+++ b/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java
@@ -17,12 +17,15 @@
import android.content.ContentResolver;
import android.content.Context;
+import android.content.res.Resources;
import android.database.ContentObserver;
+import android.graphics.BitmapFactory;
import android.os.Handler;
import android.os.Looper;
import android.provider.Settings;
import android.view.LayoutInflater;
+import com.android.keyguard.R;
import com.android.systemui.plugins.ClockPlugin;
import com.android.systemui.statusbar.policy.ExtensionController;
import com.android.systemui.statusbar.policy.ExtensionController.Extension;
@@ -45,6 +48,7 @@
private final LayoutInflater mLayoutInflater;
private final ContentResolver mContentResolver;
+ private final List<ClockInfo> mClockInfos = new ArrayList<>();
/**
* Observe settings changes to know when to switch the clock face.
*/
@@ -76,6 +80,36 @@
mExtensionController = extensionController;
mLayoutInflater = LayoutInflater.from(context);
mContentResolver = context.getContentResolver();
+
+ Resources res = context.getResources();
+ mClockInfos.add(ClockInfo.builder()
+ .setName("default")
+ .setTitle(res.getString(R.string.clock_title_default))
+ .setId("default")
+ .setThumbnail(() -> BitmapFactory.decodeResource(res, R.drawable.default_thumbnail))
+ .setPreview(() -> BitmapFactory.decodeResource(res, R.drawable.default_preview))
+ .build());
+ mClockInfos.add(ClockInfo.builder()
+ .setName("bubble")
+ .setTitle(res.getString(R.string.clock_title_bubble))
+ .setId(BubbleClockController.class.getName())
+ .setThumbnail(() -> BitmapFactory.decodeResource(res, R.drawable.bubble_thumbnail))
+ .setPreview(() -> BitmapFactory.decodeResource(res, R.drawable.bubble_preview))
+ .build());
+ mClockInfos.add(ClockInfo.builder()
+ .setName("stretch")
+ .setTitle(res.getString(R.string.clock_title_stretch))
+ .setId(StretchAnalogClockController.class.getName())
+ .setThumbnail(() -> BitmapFactory.decodeResource(res, R.drawable.stretch_thumbnail))
+ .setPreview(() -> BitmapFactory.decodeResource(res, R.drawable.stretch_preview))
+ .build());
+ mClockInfos.add(ClockInfo.builder()
+ .setName("type")
+ .setTitle(res.getString(R.string.clock_title_type))
+ .setId(TypeClockController.class.getName())
+ .setThumbnail(() -> BitmapFactory.decodeResource(res, R.drawable.type_thumbnail))
+ .setPreview(() -> BitmapFactory.decodeResource(res, R.drawable.type_preview))
+ .build());
}
/**
@@ -101,6 +135,13 @@
}
}
+ /**
+ * Get information about available clock faces.
+ */
+ List<ClockInfo> getClockInfos() {
+ return mClockInfos;
+ }
+
private void setClockPlugin(ClockPlugin plugin) {
for (int i = 0; i < mListeners.size(); i++) {
// It probably doesn't make sense to supply the same plugin instances to multiple
diff --git a/packages/SystemUI/src/com/android/keyguard/clock/ClockOptionsProvider.java b/packages/SystemUI/src/com/android/keyguard/clock/ClockOptionsProvider.java
new file mode 100644
index 0000000..5ef35be
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/clock/ClockOptionsProvider.java
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.keyguard.clock;
+
+import android.content.ContentProvider;
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.database.MatrixCursor;
+import android.graphics.Bitmap;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.ParcelFileDescriptor;
+import android.os.ParcelFileDescriptor.AutoCloseOutputStream;
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.Dependency;
+
+import java.io.FileNotFoundException;
+import java.util.List;
+import java.util.function.Supplier;
+
+/**
+ * Exposes custom clock face options and provides realistic preview images.
+ *
+ * APIs:
+ *
+ * /list_options: List the available clock faces, which has the following columns
+ * name: name of the clock face
+ * title: title of the clock face
+ * id: value used to set the clock face
+ * thumbnail: uri of the thumbnail image, should be /thumbnail/{name}
+ * preview: uri of the preview image, should be /preview/{name}
+ *
+ * /thumbnail/{id}: Opens a file stream for the thumbnail image for clock face {id}.
+ *
+ * /preview/{id}: Opens a file stream for the preview image for clock face {id}.
+ */
+public final class ClockOptionsProvider extends ContentProvider {
+
+ private static final String TAG = "ClockOptionsProvider";
+ private static final String KEY_LIST_OPTIONS = "/list_options";
+ private static final String KEY_PREVIEW = "preview";
+ private static final String KEY_THUMBNAIL = "thumbnail";
+ private static final String COLUMN_NAME = "name";
+ private static final String COLUMN_TITLE = "title";
+ private static final String COLUMN_ID = "id";
+ private static final String COLUMN_THUMBNAIL = "thumbnail";
+ private static final String COLUMN_PREVIEW = "preview";
+ private static final String MIME_TYPE_PNG = "image/png";
+ private static final String CONTENT_SCHEME = "content";
+ private static final String AUTHORITY = "com.android.keyguard.clock";
+
+ private final Supplier<List<ClockInfo>> mClocksSupplier;
+
+ public ClockOptionsProvider() {
+ this(() -> Dependency.get(ClockManager.class).getClockInfos());
+ }
+
+ @VisibleForTesting
+ ClockOptionsProvider(Supplier<List<ClockInfo>> clocksSupplier) {
+ mClocksSupplier = clocksSupplier;
+ }
+
+ @Override
+ public boolean onCreate() {
+ return true;
+ }
+
+ @Override
+ public String getType(Uri uri) {
+ List<String> segments = uri.getPathSegments();
+ if (segments.size() > 0 && (KEY_PREVIEW.equals(segments.get(0))
+ || KEY_THUMBNAIL.equals(segments.get(0)))) {
+ return MIME_TYPE_PNG;
+ }
+ return "vnd.android.cursor.dir/clock_faces";
+ }
+
+ @Override
+ public Cursor query(Uri uri, String[] projection, String selection,
+ String[] selectionArgs, String sortOrder) {
+ if (!KEY_LIST_OPTIONS.equals(uri.getPath())) {
+ return null;
+ }
+ MatrixCursor cursor = new MatrixCursor(new String[] {
+ COLUMN_NAME, COLUMN_TITLE, COLUMN_ID, COLUMN_THUMBNAIL, COLUMN_PREVIEW});
+ List<ClockInfo> clocks = mClocksSupplier.get();
+ for (int i = 0; i < clocks.size(); i++) {
+ ClockInfo clock = clocks.get(i);
+ cursor.newRow()
+ .add(COLUMN_NAME, clock.getName())
+ .add(COLUMN_TITLE, clock.getTitle())
+ .add(COLUMN_ID, clock.getId())
+ .add(COLUMN_THUMBNAIL, createThumbnailUri(clock))
+ .add(COLUMN_PREVIEW, createPreviewUri(clock));
+ }
+ return cursor;
+ }
+
+ @Override
+ public Uri insert(Uri uri, ContentValues initialValues) {
+ return null;
+ }
+
+ @Override
+ public int delete(Uri uri, String selection, String[] selectionArgs) {
+ return 0;
+ }
+
+ @Override
+ public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
+ return 0;
+ }
+
+ @Override
+ public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
+ List<String> segments = uri.getPathSegments();
+ if (segments.size() != 2 || !(KEY_PREVIEW.equals(segments.get(0))
+ || KEY_THUMBNAIL.equals(segments.get(0)))) {
+ throw new FileNotFoundException("Invalid preview url");
+ }
+ String id = segments.get(1);
+ if (TextUtils.isEmpty(id)) {
+ throw new FileNotFoundException("Invalid preview url, missing id");
+ }
+ ClockInfo clock = null;
+ List<ClockInfo> clocks = mClocksSupplier.get();
+ for (int i = 0; i < clocks.size(); i++) {
+ if (id.equals(clocks.get(i).getId())) {
+ clock = clocks.get(i);
+ break;
+ }
+ }
+ if (clock == null) {
+ throw new FileNotFoundException("Invalid preview url, id not found");
+ }
+ return openPipeHelper(uri, MIME_TYPE_PNG, null, KEY_PREVIEW.equals(segments.get(0))
+ ? clock.getPreview() : clock.getThumbnail(), new MyWriter());
+ }
+
+ private Uri createThumbnailUri(ClockInfo clock) {
+ return new Uri.Builder()
+ .scheme(CONTENT_SCHEME)
+ .authority(AUTHORITY)
+ .appendPath(KEY_THUMBNAIL)
+ .appendPath(clock.getId())
+ .build();
+ }
+
+ private Uri createPreviewUri(ClockInfo clock) {
+ return new Uri.Builder()
+ .scheme(CONTENT_SCHEME)
+ .authority(AUTHORITY)
+ .appendPath(KEY_PREVIEW)
+ .appendPath(clock.getId())
+ .build();
+ }
+
+ private static class MyWriter implements ContentProvider.PipeDataWriter<Bitmap> {
+ @Override
+ public void writeDataToPipe(ParcelFileDescriptor output, Uri uri, String mimeType,
+ Bundle opts, Bitmap bitmap) {
+ try (AutoCloseOutputStream os = new AutoCloseOutputStream(output)) {
+ bitmap.compress(Bitmap.CompressFormat.PNG, 100, os);
+ } catch (Exception e) {
+ Log.w(TAG, "fail to write to pipe", e);
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index 404f2e5..9a9a52f 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -186,7 +186,12 @@
* Set a listener to be notified of bubble expand events.
*/
public void setExpandListener(BubbleExpandListener listener) {
- mExpandListener = listener;
+ mExpandListener = ((isExpanding, key) -> {
+ if (listener != null) {
+ listener.onBubbleExpandChanged(isExpanding, key);
+ }
+ mStatusBarWindowController.setBubbleExpanded(isExpanding);
+ });
if (mStackView != null) {
mStackView.setExpandListener(mExpandListener);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java
index ffaa236..86e17f3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java
@@ -202,7 +202,8 @@
private void applyFocusableFlag(State state) {
boolean panelFocusable = state.statusBarFocusable && state.panelExpanded;
if (state.bouncerShowing && (state.keyguardOccluded || state.keyguardNeedsInput)
- || ENABLE_REMOTE_INPUT && state.remoteInputActive) {
+ || ENABLE_REMOTE_INPUT && state.remoteInputActive
+ || state.bubbleExpanded) {
mLpChanged.flags &= ~WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
mLpChanged.flags &= ~WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
} else if (state.isKeyguardShowingAndNotOccluded() || panelFocusable) {
@@ -486,6 +487,21 @@
return mCurrentState.bubblesShowing;
}
+ /**
+ * Sets if there is a bubble being expanded on the screen.
+ */
+ public void setBubbleExpanded(boolean bubbleExpanded) {
+ mCurrentState.bubbleExpanded = bubbleExpanded;
+ apply(mCurrentState);
+ }
+
+ /**
+ * The bubble is shown in expanded state for the status bar.
+ */
+ public boolean getBubbleExpanded() {
+ return mCurrentState.bubbleExpanded;
+ }
+
public void setStateListener(OtherwisedCollapsedListener listener) {
mListener = listener;
}
@@ -539,6 +555,7 @@
boolean wallpaperSupportsAmbientMode;
boolean notTouchable;
boolean bubblesShowing;
+ boolean bubbleExpanded;
/**
* The {@link StatusBar} state from the status bar.
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockInfoTest.java b/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockInfoTest.java
new file mode 100644
index 0000000..d2b2654
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockInfoTest.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.keyguard.clock;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.verify;
+
+import android.graphics.Bitmap;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper.RunWithLooper;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.function.Supplier;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@RunWithLooper
+public final class ClockInfoTest extends SysuiTestCase {
+
+ @Mock
+ private Supplier<Bitmap> mMockSupplier;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ }
+
+ @Test
+ public void testGetName() {
+ final String name = "name";
+ ClockInfo info = ClockInfo.builder().setName(name).build();
+ assertThat(info.getName()).isEqualTo(name);
+ }
+
+ @Test
+ public void testGetTitle() {
+ final String title = "title";
+ ClockInfo info = ClockInfo.builder().setTitle(title).build();
+ assertThat(info.getTitle()).isEqualTo(title);
+ }
+
+ @Test
+ public void testGetId() {
+ final String id = "id";
+ ClockInfo info = ClockInfo.builder().setId(id).build();
+ assertThat(info.getId()).isEqualTo(id);
+ }
+
+ @Test
+ public void testGetThumbnail() {
+ ClockInfo info = ClockInfo.builder().setThumbnail(mMockSupplier).build();
+ info.getThumbnail();
+ verify(mMockSupplier).get();
+ }
+
+ @Test
+ public void testGetPreview() {
+ ClockInfo info = ClockInfo.builder().setPreview(mMockSupplier).build();
+ info.getPreview();
+ verify(mMockSupplier).get();
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockOptionsProviderTest.java b/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockOptionsProviderTest.java
new file mode 100644
index 0000000..0cd6f9a
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockOptionsProviderTest.java
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.keyguard.clock;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.verify;
+
+import android.database.Cursor;
+import android.graphics.Bitmap;
+import android.net.Uri;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper.RunWithLooper;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Supplier;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@RunWithLooper
+public final class ClockOptionsProviderTest extends SysuiTestCase {
+
+ private static final String CONTENT_SCHEME = "content";
+ private static final String AUTHORITY = "com.android.keyguard.clock";
+ private static final String LIST_OPTIONS = "list_options";
+ private static final String PREVIEW = "preview";
+ private static final String THUMBNAIL = "thumbnail";
+ private static final String MIME_TYPE_LIST_OPTIONS = "vnd.android.cursor.dir/clock_faces";
+ private static final String MIME_TYPE_PNG = "image/png";
+ private static final String NAME_COLUMN = "name";
+ private static final String TITLE_COLUMN = "title";
+ private static final String ID_COLUMN = "id";
+ private static final String PREVIEW_COLUMN = "preview";
+ private static final String THUMBNAIL_COLUMN = "thumbnail";
+
+ private ClockOptionsProvider mProvider;
+ private Supplier<List<ClockInfo>> mMockSupplier;
+ private List<ClockInfo> mClocks;
+ private Uri mListOptionsUri;
+ @Mock
+ private Supplier<Bitmap> mMockBitmapSupplier;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mClocks = new ArrayList<>();
+ mProvider = new ClockOptionsProvider(() -> mClocks);
+ mListOptionsUri = new Uri.Builder()
+ .scheme(CONTENT_SCHEME)
+ .authority(AUTHORITY)
+ .appendPath(LIST_OPTIONS)
+ .build();
+ }
+
+ @Test
+ public void testGetType_listOptions() {
+ Uri uri = new Uri.Builder()
+ .scheme(CONTENT_SCHEME)
+ .authority(AUTHORITY)
+ .appendPath(LIST_OPTIONS)
+ .build();
+ assertThat(mProvider.getType(uri)).isEqualTo(MIME_TYPE_LIST_OPTIONS);
+ }
+
+ @Test
+ public void testGetType_preview() {
+ Uri uri = new Uri.Builder()
+ .scheme(CONTENT_SCHEME)
+ .authority(AUTHORITY)
+ .appendPath(PREVIEW)
+ .appendPath("id")
+ .build();
+ assertThat(mProvider.getType(uri)).isEqualTo(MIME_TYPE_PNG);
+ }
+
+ @Test
+ public void testGetType_thumbnail() {
+ Uri uri = new Uri.Builder()
+ .scheme(CONTENT_SCHEME)
+ .authority(AUTHORITY)
+ .appendPath(THUMBNAIL)
+ .appendPath("id")
+ .build();
+ assertThat(mProvider.getType(uri)).isEqualTo(MIME_TYPE_PNG);
+ }
+
+ @Test
+ public void testQuery_noClocks() {
+ Cursor cursor = mProvider.query(mListOptionsUri, null, null, null);
+ assertThat(cursor.getCount()).isEqualTo(0);
+ }
+
+ @Test
+ public void testQuery_listOptions() {
+ mClocks.add(ClockInfo.builder()
+ .setName("name_a")
+ .setTitle("title_a")
+ .setId("id_a")
+ .build());
+ mClocks.add(ClockInfo.builder()
+ .setName("name_b")
+ .setTitle("title_b")
+ .setId("id_b")
+ .build());
+ Cursor cursor = mProvider.query(mListOptionsUri, null, null, null);
+ assertThat(cursor.getCount()).isEqualTo(2);
+ cursor.moveToFirst();
+ assertThat(cursor.getString(
+ cursor.getColumnIndex(NAME_COLUMN))).isEqualTo("name_a");
+ assertThat(cursor.getString(
+ cursor.getColumnIndex(TITLE_COLUMN))).isEqualTo("title_a");
+ assertThat(cursor.getString(
+ cursor.getColumnIndex(ID_COLUMN))).isEqualTo("id_a");
+ assertThat(cursor.getString(
+ cursor.getColumnIndex(PREVIEW_COLUMN)))
+ .isEqualTo("content://com.android.keyguard.clock/preview/id_a");
+ assertThat(cursor.getString(
+ cursor.getColumnIndex(THUMBNAIL_COLUMN)))
+ .isEqualTo("content://com.android.keyguard.clock/thumbnail/id_a");
+ cursor.moveToNext();
+ assertThat(cursor.getString(
+ cursor.getColumnIndex(NAME_COLUMN))).isEqualTo("name_b");
+ assertThat(cursor.getString(
+ cursor.getColumnIndex(TITLE_COLUMN))).isEqualTo("title_b");
+ assertThat(cursor.getString(
+ cursor.getColumnIndex(ID_COLUMN))).isEqualTo("id_b");
+ assertThat(cursor.getString(
+ cursor.getColumnIndex(PREVIEW_COLUMN)))
+ .isEqualTo("content://com.android.keyguard.clock/preview/id_b");
+ assertThat(cursor.getString(
+ cursor.getColumnIndex(THUMBNAIL_COLUMN)))
+ .isEqualTo("content://com.android.keyguard.clock/thumbnail/id_b");
+ }
+
+ @Test
+ public void testOpenFile_preview() throws Exception {
+ mClocks.add(ClockInfo.builder()
+ .setId("id")
+ .setPreview(mMockBitmapSupplier)
+ .build());
+ Uri uri = new Uri.Builder()
+ .scheme(CONTENT_SCHEME)
+ .authority(AUTHORITY)
+ .appendPath(PREVIEW)
+ .appendPath("id")
+ .build();
+ mProvider.openFile(uri, "r").close();
+ verify(mMockBitmapSupplier).get();
+ }
+
+ @Test
+ public void testOpenFile_thumbnail() throws Exception {
+ mClocks.add(ClockInfo.builder()
+ .setId("id")
+ .setThumbnail(mMockBitmapSupplier)
+ .build());
+ Uri uri = new Uri.Builder()
+ .scheme(CONTENT_SCHEME)
+ .authority(AUTHORITY)
+ .appendPath(THUMBNAIL)
+ .appendPath("id")
+ .build();
+ mProvider.openFile(uri, "r").close();
+ verify(mMockBitmapSupplier).get();
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
index e32d48d..49b4641 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
@@ -168,12 +168,14 @@
// We should have bubbles & their notifs should show in the shade
assertTrue(mBubbleController.hasBubbles());
assertTrue(mRow.getEntry().showInShadeWhenBubble());
+ assertFalse(mStatusBarWindowController.getBubbleExpanded());
// Expand the stack
BubbleStackView stackView = mBubbleController.getStackView();
stackView.expandStack();
assertTrue(mBubbleController.isStackExpanded());
verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getEntry().key);
+ assertTrue(mStatusBarWindowController.getBubbleExpanded());
// Make sure it's no longer in the shade
assertFalse(mRow.getEntry().showInShadeWhenBubble());
@@ -182,6 +184,7 @@
stackView.collapseStack();
verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow.getEntry().key);
assertFalse(mBubbleController.isStackExpanded());
+ assertFalse(mStatusBarWindowController.getBubbleExpanded());
}
@Test
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index d20508a..9d9721d 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -4697,7 +4697,7 @@
mSettings.getCurrentUserId(),
mContext.getBasePackageName());
nextIme = mSettings.getSelectedInputMethod();
- nextEnabledImes = getEnabledInputMethodList();
+ nextEnabledImes = mSettings.getEnabledInputMethodListLocked();
final PrintWriter pr = shellCommand.getOutPrintWriter();
pr.println("Reset current and enabled IMEs");
pr.println("Newly selected IME:");
diff --git a/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java b/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java
index f736056..1dada92 100644
--- a/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java
+++ b/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java
@@ -17,8 +17,10 @@
package com.android.server.os;
import android.annotation.RequiresPermission;
+import android.app.ActivityManager;
import android.app.AppOpsManager;
import android.content.Context;
+import android.content.pm.UserInfo;
import android.os.Binder;
import android.os.BugreportParams;
import android.os.IDumpstate;
@@ -28,26 +30,29 @@
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.SystemProperties;
+import android.os.UserManager;
import android.util.Slog;
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.Preconditions;
+
import java.io.FileDescriptor;
// TODO(b/111441001):
-// 1. Handle the case where another bugreport is in progress
-// 2. Make everything threadsafe
-// 3. Pass validation & other errors on listener
+// Intercept onFinished() & implement death recipient here and shutdown
+// bugreportd service.
/**
* Implementation of the service that provides a privileged API to capture and consume bugreports.
*
- * <p>Delegates the actualy generation to a native implementation of {@code Dumpstate}.
+ * <p>Delegates the actualy generation to a native implementation of {@code IDumpstate}.
*/
class BugreportManagerServiceImpl extends IDumpstate.Stub {
private static final String TAG = "BugreportManagerService";
private static final String BUGREPORT_SERVICE = "bugreportd";
private static final long DEFAULT_BUGREPORT_SERVICE_TIMEOUT_MILLIS = 30 * 1000;
- private IDumpstate mDs = null;
+ private final Object mLock = new Object();
private final Context mContext;
private final AppOpsManager mAppOps;
@@ -59,43 +64,44 @@
@Override
@RequiresPermission(android.Manifest.permission.DUMP)
public IDumpstateToken setListener(String name, IDumpstateListener listener,
- boolean getSectionDetails) throws RemoteException {
- // TODO(b/111441001): Figure out if lazy setting of listener should be allowed
- // and if so how to handle it.
+ boolean getSectionDetails) {
throw new UnsupportedOperationException("setListener is not allowed on this service");
}
- // TODO(b/111441001): Intercept onFinished here in system server and shutdown
- // the bugreportd service.
@Override
@RequiresPermission(android.Manifest.permission.DUMP)
public void startBugreport(int callingUidUnused, String callingPackage,
FileDescriptor bugreportFd, FileDescriptor screenshotFd,
- int bugreportMode, IDumpstateListener listener) throws RemoteException {
- int callingUid = Binder.getCallingUid();
- // TODO(b/111441001): validate all arguments & ensure primary user
- validate(bugreportMode);
+ int bugreportMode, IDumpstateListener listener) {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, "startBugreport");
+ Preconditions.checkNotNull(callingPackage);
+ Preconditions.checkNotNull(bugreportFd);
+ Preconditions.checkNotNull(listener);
+ validateBugreportMode(bugreportMode);
+ ensureIsPrimaryUser();
+ int callingUid = Binder.getCallingUid();
mAppOps.checkPackage(callingUid, callingPackage);
- mDs = getDumpstateService();
- if (mDs == null) {
- Slog.w(TAG, "Unable to get bugreport service");
- // TODO(b/111441001): pass error on listener
- return;
+
+ synchronized (mLock) {
+ startBugreportLocked(callingUid, callingPackage, bugreportFd, screenshotFd,
+ bugreportMode, listener);
}
- mDs.startBugreport(callingUid, callingPackage,
- bugreportFd, screenshotFd, bugreportMode, listener);
}
@Override
@RequiresPermission(android.Manifest.permission.DUMP)
- public void cancelBugreport() throws RemoteException {
- // This tells init to cancel bugreportd service.
- SystemProperties.set("ctl.stop", BUGREPORT_SERVICE);
- mDs = null;
+ public void cancelBugreport() {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, "startBugreport");
+ // This tells init to cancel bugreportd service. Note that this is achieved through setting
+ // a system property which is not thread-safe. So the lock here offers thread-safety only
+ // among callers of the API.
+ synchronized (mLock) {
+ SystemProperties.set("ctl.stop", BUGREPORT_SERVICE);
+ }
}
- private boolean validate(@BugreportParams.BugreportMode int mode) {
+ private void validateBugreportMode(@BugreportParams.BugreportMode int mode) {
if (mode != BugreportParams.BUGREPORT_MODE_FULL
&& mode != BugreportParams.BUGREPORT_MODE_INTERACTIVE
&& mode != BugreportParams.BUGREPORT_MODE_REMOTE
@@ -103,9 +109,66 @@
&& mode != BugreportParams.BUGREPORT_MODE_TELEPHONY
&& mode != BugreportParams.BUGREPORT_MODE_WIFI) {
Slog.w(TAG, "Unknown bugreport mode: " + mode);
- return false;
+ throw new IllegalArgumentException("Unknown bugreport mode: " + mode);
}
- return true;
+ }
+
+ /**
+ * Validates that the current user is the primary user.
+ *
+ * @throws IllegalArgumentException if the current user is not the primary user
+ */
+ private void ensureIsPrimaryUser() {
+ UserInfo currentUser = null;
+ try {
+ currentUser = ActivityManager.getService().getCurrentUser();
+ } catch (RemoteException e) {
+ // Impossible to get RemoteException for an in-process call.
+ }
+
+ UserInfo primaryUser = UserManager.get(mContext).getPrimaryUser();
+ if (currentUser == null) {
+ logAndThrow("No current user. Only primary user is allowed to take bugreports.");
+ }
+ if (primaryUser == null) {
+ logAndThrow("No primary user. Only primary user is allowed to take bugreports.");
+ }
+ if (primaryUser.id != currentUser.id) {
+ logAndThrow("Current user not primary user. Only primary user"
+ + " is allowed to take bugreports.");
+ }
+ }
+
+ @GuardedBy("mLock")
+ private void startBugreportLocked(int callingUid, String callingPackage,
+ FileDescriptor bugreportFd, FileDescriptor screenshotFd,
+ int bugreportMode, IDumpstateListener listener) {
+ if (isDumpstateBinderServiceRunningLocked()) {
+ Slog.w(TAG, "'dumpstate' is already running. Cannot start a new bugreport"
+ + " while another one is currently in progress.");
+ // TODO(b/111441001): Use a new error code; add this to the documentation of the API.
+ reportError(listener, IDumpstateListener.BUGREPORT_ERROR_RUNTIME_ERROR);
+ return;
+ }
+
+ IDumpstate ds = startAndGetDumpstateBinderServiceLocked();
+ if (ds == null) {
+ Slog.w(TAG, "Unable to get bugreport service");
+ reportError(listener, IDumpstateListener.BUGREPORT_ERROR_RUNTIME_ERROR);
+ return;
+ }
+ try {
+ ds.startBugreport(callingUid, callingPackage,
+ bugreportFd, screenshotFd, bugreportMode, listener);
+ } catch (RemoteException e) {
+ reportError(listener, IDumpstateListener.BUGREPORT_ERROR_RUNTIME_ERROR);
+ }
+ }
+
+ @GuardedBy("mLock")
+ private boolean isDumpstateBinderServiceRunningLocked() {
+ IDumpstate ds = IDumpstate.Stub.asInterface(ServiceManager.getService("dumpstate"));
+ return ds != null;
}
/*
@@ -115,8 +178,12 @@
* <p>Generating bugreports requires root privileges. To limit the footprint
* of the root access, the actual generation in Dumpstate binary is accessed as a
* oneshot service 'bugreport'.
+ *
+ * <p>Note that starting the service is achieved through setting a system property, which is
+ * not thread-safe. So the lock here offers thread-safety only among callers of the API.
*/
- private IDumpstate getDumpstateService() {
+ @GuardedBy("mLock")
+ private IDumpstate startAndGetDumpstateBinderServiceLocked() {
// Start bugreport service.
SystemProperties.set("ctl.start", BUGREPORT_SERVICE);
@@ -145,4 +212,18 @@
}
return ds;
}
+
+ private void reportError(IDumpstateListener listener, int errorCode) {
+ try {
+ listener.onError(errorCode);
+ } catch (RemoteException e) {
+ // Something went wrong in binder or app process. There's nothing to do here.
+ Slog.w(TAG, "onError() transaction threw RemoteException: " + e.getMessage());
+ }
+ }
+
+ private void logAndThrow(String message) {
+ Slog.w(TAG, message);
+ throw new IllegalArgumentException(message);
+ }
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index a0a2f6c..e18da7f 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -3941,13 +3941,13 @@
if (apex != null) {
try {
final ApexInfo activePkg = apex.getActivePackage(packageName);
- if (activePkg != null) {
+ if (activePkg != null && !TextUtils.isEmpty(activePkg.packagePath)) {
try {
return PackageParser.generatePackageInfoFromApex(
new File(activePkg.packagePath), true /* collect certs */);
} catch (PackageParserException pe) {
- throw new IllegalStateException("Unable to parse: " + activePkg,
- pe);
+ Log.e(TAG, "Unable to parse package at "
+ + activePkg.packagePath, pe);
}
}
} catch (RemoteException e) {
@@ -13426,6 +13426,10 @@
return false;
}
+ if ((installFlags & PackageManager.INSTALL_DISABLE_VERIFICATION) != 0) {
+ return false;
+ }
+
boolean ensureVerifyAppsEnabled = isUserRestricted(userId, UserManager.ENSURE_VERIFY_APPS);
// Check if installing from ADB
diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java
index 0371663..cc6cfbb 100644
--- a/services/core/java/com/android/server/pm/StagingManager.java
+++ b/services/core/java/com/android/server/pm/StagingManager.java
@@ -186,6 +186,7 @@
private void preRebootVerification(@NonNull PackageInstallerSession session) {
boolean success = true;
+ // STOPSHIP: TODO(b/123753157): Verify APKs through Package Verifier.
if (!sessionContainsApex(session)) {
// TODO: Decide whether we want to fail fast by detecting signature mismatches for APKs,
// right away.
@@ -336,6 +337,7 @@
PackageInstaller.SessionParams params = originalSession.params.copy();
params.isStaged = false;
+ params.installFlags |= PackageManager.INSTALL_DISABLE_VERIFICATION;
int apkSessionId = mPi.createSession(
params, originalSession.getInstallerPackageName(), originalSession.userId);
PackageInstallerSession apkSession = mPi.getSession(apkSessionId);
diff --git a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
index 8e04160..3c6a54a 100644
--- a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
+++ b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
@@ -26,6 +26,7 @@
import android.os.Handler;
import android.os.HandlerThread;
import android.text.TextUtils;
+import android.util.Pair;
import android.util.Slog;
import android.util.StatsLog;
@@ -64,10 +65,8 @@
return PackageHealthObserverImpact.USER_IMPACT_NONE;
}
- RollbackInfo rollback =
- getAvailableMainlineRollback(mContext.getSystemService(RollbackManager.class),
- failedPackage, moduleMetadataPackage);
- if (rollback == null) {
+ if (getAvailableRollback(mContext.getSystemService(RollbackManager.class),
+ failedPackage, moduleMetadataPackage) == null) {
// Don't handle the notification, no rollbacks available for the package
return PackageHealthObserverImpact.USER_IMPACT_NONE;
}
@@ -84,38 +83,46 @@
}
RollbackManager rollbackManager = mContext.getSystemService(RollbackManager.class);
- RollbackInfo rollback = getAvailableMainlineRollback(rollbackManager,
+ Pair<RollbackInfo, Boolean> rollbackPair = getAvailableRollback(rollbackManager,
failedPackage, moduleMetadataPackage);
- if (rollback == null) {
- Slog.w(TAG, "Expected rollback but no mainline rollback found for package: [ "
+ if (rollbackPair == null) {
+ Slog.w(TAG, "Expected rollback but no valid rollback found for package: [ "
+ failedPackage.getPackageName() + "] with versionCode: ["
+ failedPackage.getVersionCode() + "]");
return false;
}
+ RollbackInfo rollback = rollbackPair.first;
+ // We only log mainline package rollbacks, so check if rollback contains the
+ // module metadata provider, if it does, the rollback is a mainline rollback
+ boolean hasModuleMetadataPackage = rollbackPair.second;
- StatsLog.write(StatsLog.WATCHDOG_ROLLBACK_OCCURRED,
- StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_INITIATE,
- moduleMetadataPackage.getPackageName(),
- moduleMetadataPackage.getVersionCode());
+ if (hasModuleMetadataPackage) {
+ StatsLog.write(StatsLog.WATCHDOG_ROLLBACK_OCCURRED,
+ StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_INITIATE,
+ moduleMetadataPackage.getPackageName(),
+ moduleMetadataPackage.getVersionCode());
+ }
LocalIntentReceiver rollbackReceiver = new LocalIntentReceiver((Intent result) -> {
- int status = result.getIntExtra(RollbackManager.EXTRA_STATUS,
- RollbackManager.STATUS_FAILURE);
- if (status == RollbackManager.STATUS_SUCCESS) {
- StatsLog.write(StatsLog.WATCHDOG_ROLLBACK_OCCURRED,
- StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS,
- moduleMetadataPackage.getPackageName(),
- moduleMetadataPackage.getVersionCode());
- } else {
- StatsLog.write(StatsLog.WATCHDOG_ROLLBACK_OCCURRED,
- StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_FAILURE,
- moduleMetadataPackage.getPackageName(),
- moduleMetadataPackage.getVersionCode());
+ if (hasModuleMetadataPackage) {
+ int status = result.getIntExtra(RollbackManager.EXTRA_STATUS,
+ RollbackManager.STATUS_FAILURE);
+ if (status == RollbackManager.STATUS_SUCCESS) {
+ StatsLog.write(StatsLog.WATCHDOG_ROLLBACK_OCCURRED,
+ StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS,
+ moduleMetadataPackage.getPackageName(),
+ moduleMetadataPackage.getVersionCode());
+ } else {
+ StatsLog.write(StatsLog.WATCHDOG_ROLLBACK_OCCURRED,
+ StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_FAILURE,
+ moduleMetadataPackage.getPackageName(),
+ moduleMetadataPackage.getVersionCode());
+ }
}
});
mHandler.post(() ->
rollbackManager.commitRollback(rollback.getRollbackId(),
- Collections.singletonList(moduleMetadataPackage),
+ Collections.singletonList(failedPackage),
rollbackReceiver.getIntentSender()));
// Assume rollback executed successfully
return true;
@@ -134,7 +141,7 @@
PackageWatchdog.getInstance(mContext).startObservingHealth(this, packages, durationMs);
}
- private RollbackInfo getAvailableMainlineRollback(RollbackManager rollbackManager,
+ private Pair<RollbackInfo, Boolean> getAvailableRollback(RollbackManager rollbackManager,
VersionedPackage failedPackage, VersionedPackage moduleMetadataPackage) {
for (RollbackInfo rollback : rollbackManager.getAvailableRollbacks()) {
// We only rollback mainline packages, so check if rollback contains the
@@ -149,8 +156,8 @@
&& packageRollback.getVersionRolledBackFrom().getVersionCode()
== failedPackage.getVersionCode();
}
- if (hasModuleMetadataPackage && hasFailedPackage) {
- return rollback;
+ if (hasFailedPackage) {
+ return new Pair<RollbackInfo, Boolean>(rollback, hasModuleMetadataPackage);
}
}
return null;
@@ -159,7 +166,7 @@
private VersionedPackage getModuleMetadataPackage() {
String packageName = mContext.getResources().getString(
R.string.config_defaultModuleMetadataProvider);
- if (!TextUtils.isEmpty(packageName)) {
+ if (TextUtils.isEmpty(packageName)) {
return null;
}
diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java
index 0f7407b..3586772 100644
--- a/services/core/java/com/android/server/stats/StatsCompanionService.java
+++ b/services/core/java/com/android/server/stats/StatsCompanionService.java
@@ -1780,8 +1780,8 @@
long elapsedNanos, final long wallClockNanos, List<StatsLogEventWrapper> pulledData) {
StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
final long elapsedMillis = SystemClock.elapsedRealtime();
- // Fails every 10 buckets.
- if (mDebugFailingElapsedClockPullCount++ % 10 == 0) {
+ // Fails every 5 buckets.
+ if (mDebugFailingElapsedClockPullCount++ % 5 == 0) {
mDebugFailingElapsedClockPreviousValue = elapsedMillis;
throw new RuntimeException("Failing debug elapsed clock");
}
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index 3729eaf..5c7b287 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -127,6 +127,7 @@
"android.frameworks.schedulerservice@1.0",
"android.frameworks.sensorservice@1.0",
"android.system.suspend@1.0",
+ "suspend_control_aidl_interface-cpp",
],
static_libs: [
diff --git a/services/core/jni/com_android_server_am_BatteryStatsService.cpp b/services/core/jni/com_android_server_am_BatteryStatsService.cpp
index 024760d..5c19ad3 100644
--- a/services/core/jni/com_android_server_am_BatteryStatsService.cpp
+++ b/services/core/jni/com_android_server_am_BatteryStatsService.cpp
@@ -32,8 +32,8 @@
#include <android/hardware/power/1.0/IPower.h>
#include <android/hardware/power/1.1/IPower.h>
#include <android/hardware/power/stats/1.0/IPowerStats.h>
-#include <android/system/suspend/1.0/ISystemSuspend.h>
-#include <android/system/suspend/1.0/ISystemSuspendCallback.h>
+#include <android/system/suspend/BnSuspendCallback.h>
+#include <android/system/suspend/ISuspendControlService.h>
#include <android_runtime/AndroidRuntime.h>
#include <jni.h>
@@ -46,14 +46,14 @@
using android::hardware::Return;
using android::hardware::Void;
+using android::system::suspend::BnSuspendCallback;
using android::hardware::power::V1_0::PowerStatePlatformSleepState;
using android::hardware::power::V1_0::PowerStateVoter;
using android::hardware::power::V1_0::Status;
using android::hardware::power::V1_1::PowerStateSubsystem;
using android::hardware::power::V1_1::PowerStateSubsystemSleepState;
using android::hardware::hidl_vec;
-using android::system::suspend::V1_0::ISystemSuspend;
-using android::system::suspend::V1_0::ISystemSuspendCallback;
+using android::system::suspend::ISuspendControlService;
using IPowerV1_1 = android::hardware::power::V1_1::IPower;
using IPowerV1_0 = android::hardware::power::V1_0::IPower;
@@ -68,7 +68,7 @@
extern sp<IPowerV1_0> getPowerHalV1_0();
extern sp<IPowerV1_1> getPowerHalV1_1();
extern bool processPowerHalReturn(const Return<void> &ret, const char* functionName);
-extern sp<ISystemSuspend> getSuspendHal();
+extern sp<ISuspendControlService> getSuspendControl();
// Java methods used in getLowPowerStats
static jmethodID jgetAndUpdatePlatformState = NULL;
@@ -103,17 +103,17 @@
sp<PowerHalDeathRecipient> gDeathRecipient = new PowerHalDeathRecipient();
-class WakeupCallback : public ISystemSuspendCallback {
-public:
- Return<void> notifyWakeup(bool success) override {
- ALOGV("In wakeup_callback: %s", success ? "resumed from suspend" : "suspend aborted");
+class WakeupCallback : public BnSuspendCallback {
+ public:
+ binder::Status notifyWakeup(bool success) override {
+ ALOGI("In wakeup_callback: %s", success ? "resumed from suspend" : "suspend aborted");
int ret = sem_post(&wakeup_sem);
if (ret < 0) {
char buf[80];
strerror_r(errno, buf, sizeof(buf));
ALOGE("Error posting wakeup sem: %s\n", buf);
}
- return Void();
+ return binder::Status::ok();
}
};
@@ -136,9 +136,12 @@
jniThrowException(env, "java/lang/IllegalStateException", buf);
return -1;
}
- ALOGV("Registering callback...");
- sp<ISystemSuspend> suspendHal = getSuspendHal();
- suspendHal->registerCallback(new WakeupCallback());
+ sp<ISuspendControlService> suspendControl = getSuspendControl();
+ bool isRegistered = false;
+ suspendControl->registerCallback(new WakeupCallback(), &isRegistered);
+ if (!isRegistered) {
+ ALOGE("Failed to register wakeup callback");
+ }
}
// Wait for wakeup.
diff --git a/services/core/jni/com_android_server_power_PowerManagerService.cpp b/services/core/jni/com_android_server_power_PowerManagerService.cpp
index 0c9b5f4..9be728b 100644
--- a/services/core/jni/com_android_server_power_PowerManagerService.cpp
+++ b/services/core/jni/com_android_server_power_PowerManagerService.cpp
@@ -20,6 +20,7 @@
#include <android/hardware/power/1.1/IPower.h>
#include <android/system/suspend/1.0/ISystemSuspend.h>
+#include <android/system/suspend/ISuspendControlService.h>
#include <nativehelper/JNIHelp.h>
#include "jni.h"
@@ -30,13 +31,14 @@
#include <android-base/chrono_utils.h>
#include <android_runtime/AndroidRuntime.h>
#include <android_runtime/Log.h>
+#include <binder/IServiceManager.h>
+#include <hardware/power.h>
+#include <hardware_legacy/power.h>
+#include <hidl/ServiceManagement.h>
#include <utils/Timers.h>
#include <utils/misc.h>
#include <utils/String8.h>
#include <utils/Log.h>
-#include <hardware/power.h>
-#include <hardware_legacy/power.h>
-#include <hidl/ServiceManagement.h>
#include "com_android_server_power_PowerManagerService.h"
@@ -48,6 +50,7 @@
using android::system::suspend::V1_0::ISystemSuspend;
using android::system::suspend::V1_0::IWakeLock;
using android::system::suspend::V1_0::WakeLockType;
+using android::system::suspend::ISuspendControlService;
using IPowerV1_1 = android::hardware::power::V1_1::IPower;
using IPowerV1_0 = android::hardware::power::V1_0::IPower;
@@ -176,6 +179,7 @@
}
static sp<ISystemSuspend> gSuspendHal = nullptr;
+static sp<ISuspendControlService> gSuspendControl = nullptr;
static sp<IWakeLock> gSuspendBlocker = nullptr;
static std::mutex gSuspendMutex;
@@ -191,18 +195,33 @@
return gSuspendHal;
}
+sp<ISuspendControlService> getSuspendControl() {
+ static std::once_flag suspendControlFlag;
+ std::call_once(suspendControlFlag, [](){
+ while(gSuspendControl == nullptr) {
+ sp<IBinder> control =
+ defaultServiceManager()->getService(String16("suspend_control"));
+ if (control != nullptr) {
+ gSuspendControl = interface_cast<ISuspendControlService>(control);
+ }
+ }
+ });
+ return gSuspendControl;
+}
+
void enableAutoSuspend() {
static bool enabled = false;
-
- std::lock_guard<std::mutex> lock(gSuspendMutex);
if (!enabled) {
- sp<ISystemSuspend> suspendHal = getSuspendHal();
- suspendHal->enableAutosuspend();
- enabled = true;
+ sp<ISuspendControlService> suspendControl = getSuspendControl();
+ suspendControl->enableAutosuspend(&enabled);
}
- if (gSuspendBlocker) {
- gSuspendBlocker->release();
- gSuspendBlocker.clear();
+
+ {
+ std::lock_guard<std::mutex> lock(gSuspendMutex);
+ if (gSuspendBlocker) {
+ gSuspendBlocker->release();
+ gSuspendBlocker.clear();
+ }
}
}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index caeedee..ab30cda 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -1285,47 +1285,45 @@
}
traceEnd();
- if (!mOnlyCore) {
- if (context.getPackageManager().hasSystemFeature(
- PackageManager.FEATURE_WIFI)) {
- // Wifi Service must be started first for wifi-related services.
- traceBeginAndSlog("StartWifi");
- mSystemServiceManager.startService(WIFI_SERVICE_CLASS);
- traceEnd();
- traceBeginAndSlog("StartWifiScanning");
- mSystemServiceManager.startService(
- "com.android.server.wifi.scanner.WifiScanningService");
- traceEnd();
- }
+ if (context.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_WIFI)) {
+ // Wifi Service must be started first for wifi-related services.
+ traceBeginAndSlog("StartWifi");
+ mSystemServiceManager.startService(WIFI_SERVICE_CLASS);
+ traceEnd();
+ traceBeginAndSlog("StartWifiScanning");
+ mSystemServiceManager.startService(
+ "com.android.server.wifi.scanner.WifiScanningService");
+ traceEnd();
+ }
- if (context.getPackageManager().hasSystemFeature(
- PackageManager.FEATURE_WIFI_RTT)) {
- traceBeginAndSlog("StartRttService");
- mSystemServiceManager.startService(
- "com.android.server.wifi.rtt.RttService");
- traceEnd();
- }
+ if (context.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_WIFI_RTT)) {
+ traceBeginAndSlog("StartRttService");
+ mSystemServiceManager.startService(
+ "com.android.server.wifi.rtt.RttService");
+ traceEnd();
+ }
- if (context.getPackageManager().hasSystemFeature(
- PackageManager.FEATURE_WIFI_AWARE)) {
- traceBeginAndSlog("StartWifiAware");
- mSystemServiceManager.startService(WIFI_AWARE_SERVICE_CLASS);
- traceEnd();
- }
+ if (context.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_WIFI_AWARE)) {
+ traceBeginAndSlog("StartWifiAware");
+ mSystemServiceManager.startService(WIFI_AWARE_SERVICE_CLASS);
+ traceEnd();
+ }
- if (context.getPackageManager().hasSystemFeature(
- PackageManager.FEATURE_WIFI_DIRECT)) {
- traceBeginAndSlog("StartWifiP2P");
- mSystemServiceManager.startService(WIFI_P2P_SERVICE_CLASS);
- traceEnd();
- }
+ if (context.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_WIFI_DIRECT)) {
+ traceBeginAndSlog("StartWifiP2P");
+ mSystemServiceManager.startService(WIFI_P2P_SERVICE_CLASS);
+ traceEnd();
+ }
- if (context.getPackageManager().hasSystemFeature(
- PackageManager.FEATURE_LOWPAN)) {
- traceBeginAndSlog("StartLowpan");
- mSystemServiceManager.startService(LOWPAN_SERVICE_CLASS);
- traceEnd();
- }
+ if (context.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_LOWPAN)) {
+ traceBeginAndSlog("StartLowpan");
+ mSystemServiceManager.startService(LOWPAN_SERVICE_CLASS);
+ traceEnd();
}
if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_ETHERNET) ||
diff --git a/startop/view_compiler/Android.bp b/startop/view_compiler/Android.bp
index f5b4308..37caeb2 100644
--- a/startop/view_compiler/Android.bp
+++ b/startop/view_compiler/Android.bp
@@ -58,6 +58,8 @@
"util.cc",
"layout_validation.cc",
],
+ // b/123880763, clang-tidy analyzer has segmentation fault with dex_builder.cc
+ tidy_checks: ["-clang-analyzer-*"],
host_supported: true,
}
diff --git a/telephony/java/android/telephony/AvailableNetworkInfo.java b/telephony/java/android/telephony/AvailableNetworkInfo.java
index 4da79b3..b407b2a 100644
--- a/telephony/java/android/telephony/AvailableNetworkInfo.java
+++ b/telephony/java/android/telephony/AvailableNetworkInfo.java
@@ -114,7 +114,7 @@
in.readStringList(mMccMncs);
}
- public AvailableNetworkInfo(int subId, int priority, ArrayList<String> mccMncs) {
+ public AvailableNetworkInfo(int subId, int priority, List<String> mccMncs) {
mSubId = subId;
mPriority = priority;
mMccMncs = new ArrayList<String>(mccMncs);
diff --git a/telephony/java/android/telephony/CallAttributes.java b/telephony/java/android/telephony/CallAttributes.java
index a4cce9c..0d4f09f 100644
--- a/telephony/java/android/telephony/CallAttributes.java
+++ b/telephony/java/android/telephony/CallAttributes.java
@@ -117,9 +117,9 @@
CallAttributes s = (CallAttributes) o;
- return (mPreciseCallState == s.mPreciseCallState
+ return (Objects.equals(mPreciseCallState, s.mPreciseCallState)
&& mNetworkType == s.mNetworkType
- && mCallQuality == s.mCallQuality);
+ && Objects.equals(mCallQuality, s.mCallQuality));
}
/**
diff --git a/telephony/java/android/telephony/PreciseCallState.java b/telephony/java/android/telephony/PreciseCallState.java
index 59f3e1f..19e1931 100644
--- a/telephony/java/android/telephony/PreciseCallState.java
+++ b/telephony/java/android/telephony/PreciseCallState.java
@@ -287,11 +287,11 @@
return false;
}
PreciseCallState other = (PreciseCallState) obj;
- return (mRingingCallState != other.mRingingCallState &&
- mForegroundCallState != other.mForegroundCallState &&
- mBackgroundCallState != other.mBackgroundCallState &&
- mDisconnectCause != other.mDisconnectCause &&
- mPreciseDisconnectCause != other.mPreciseDisconnectCause);
+ return (mRingingCallState == other.mRingingCallState
+ && mForegroundCallState == other.mForegroundCallState
+ && mBackgroundCallState == other.mBackgroundCallState
+ && mDisconnectCause == other.mDisconnectCause
+ && mPreciseDisconnectCause == other.mPreciseDisconnectCause);
}
@Override
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 7c3bde4..f2a9340 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -7974,9 +7974,7 @@
* support for the feature and device firmware support.
*
* @return {@code true} if the device and carrier both support RTT, {@code false} otherwise.
- * @hide
*/
- @TestApi
public boolean isRttSupported() {
try {
ITelephony telephony = getITelephony();
@@ -9702,10 +9700,10 @@
*
* <p>
* Requires Permission:
- * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+ * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE}
* @hide
*/
- @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+ @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public boolean isOpportunisticNetworkEnabled() {
String pkgForDebug = mContext != null ? mContext.getOpPackageName() : "<unknown>";
boolean isEnabled = false;
@@ -10093,12 +10091,17 @@
* Get preferred opportunistic data subscription Id
*
* <p>Requires that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}),
- * or has permission {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}.
+ * or has either READ_PRIVILEGED_PHONE_STATE
+ * or {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} permission.
* @return subId preferred opportunistic subscription id or
* {@link SubscriptionManager#DEFAULT_SUBSCRIPTION_ID} if there are no preferred
* subscription id
*
*/
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+ android.Manifest.permission.READ_PHONE_STATE
+ })
public int getPreferredOpportunisticDataSubscription() {
String pkgForDebug = mContext != null ? mContext.getOpPackageName() : "<unknown>";
int subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
diff --git a/telephony/java/android/telephony/ims/ImsReasonInfo.java b/telephony/java/android/telephony/ims/ImsReasonInfo.java
index 4d95e55..d8d2d9e 100644
--- a/telephony/java/android/telephony/ims/ImsReasonInfo.java
+++ b/telephony/java/android/telephony/ims/ImsReasonInfo.java
@@ -465,7 +465,7 @@
public static final int CODE_USER_REJECTED_SESSION_MODIFICATION = 511;
/**
- * Upgrade Downgrade request cacncelled by the user who initiated it
+ * Upgrade Downgrade request cancelled by the user who initiated it
*/
public static final int CODE_USER_CANCELLED_SESSION_MODIFICATION = 512;
@@ -887,6 +887,185 @@
public static final int CODE_OEM_CAUSE_15 = 0xf00f;
/**
+ * @hide
+ */
+ @IntDef(value = {
+ CODE_UNSPECIFIED,
+ CODE_LOCAL_ILLEGAL_ARGUMENT,
+ CODE_LOCAL_ILLEGAL_STATE,
+ CODE_LOCAL_INTERNAL_ERROR,
+ CODE_LOCAL_IMS_SERVICE_DOWN,
+ CODE_LOCAL_NO_PENDING_CALL,
+ CODE_LOCAL_ENDED_BY_CONFERENCE_MERGE,
+ CODE_LOCAL_POWER_OFF,
+ CODE_LOCAL_LOW_BATTERY,
+ CODE_LOCAL_NETWORK_NO_SERVICE,
+ CODE_LOCAL_NETWORK_NO_LTE_COVERAGE,
+ CODE_LOCAL_NETWORK_ROAMING,
+ CODE_LOCAL_NETWORK_IP_CHANGED,
+ CODE_LOCAL_SERVICE_UNAVAILABLE,
+ CODE_LOCAL_NOT_REGISTERED,
+ CODE_LOCAL_CALL_EXCEEDED,
+ CODE_LOCAL_CALL_BUSY,
+ CODE_LOCAL_CALL_DECLINE,
+ CODE_LOCAL_CALL_VCC_ON_PROGRESSING,
+ CODE_LOCAL_CALL_RESOURCE_RESERVATION_FAILED,
+ CODE_LOCAL_CALL_CS_RETRY_REQUIRED,
+ CODE_LOCAL_CALL_VOLTE_RETRY_REQUIRED,
+ CODE_LOCAL_CALL_TERMINATED,
+ CODE_LOCAL_HO_NOT_FEASIBLE,
+ CODE_TIMEOUT_1XX_WAITING,
+ CODE_TIMEOUT_NO_ANSWER,
+ CODE_TIMEOUT_NO_ANSWER_CALL_UPDATE,
+ CODE_CALL_BARRED,
+ CODE_FDN_BLOCKED,
+ CODE_IMEI_NOT_ACCEPTED,
+ CODE_DIAL_MODIFIED_TO_USSD,
+ CODE_DIAL_MODIFIED_TO_SS,
+ CODE_DIAL_MODIFIED_TO_DIAL,
+ CODE_DIAL_MODIFIED_TO_DIAL_VIDEO,
+ CODE_DIAL_VIDEO_MODIFIED_TO_DIAL,
+ CODE_DIAL_VIDEO_MODIFIED_TO_DIAL_VIDEO,
+ CODE_DIAL_VIDEO_MODIFIED_TO_SS,
+ CODE_DIAL_VIDEO_MODIFIED_TO_USSD,
+ CODE_SIP_REDIRECTED,
+ CODE_SIP_BAD_REQUEST,
+ CODE_SIP_FORBIDDEN,
+ CODE_SIP_NOT_FOUND,
+ CODE_SIP_NOT_SUPPORTED,
+ CODE_SIP_REQUEST_TIMEOUT,
+ CODE_SIP_TEMPRARILY_UNAVAILABLE,
+ CODE_SIP_BAD_ADDRESS,
+ CODE_SIP_BUSY,
+ CODE_SIP_REQUEST_CANCELLED,
+ CODE_SIP_NOT_ACCEPTABLE,
+ CODE_SIP_NOT_REACHABLE,
+ CODE_SIP_CLIENT_ERROR,
+ CODE_SIP_TRANSACTION_DOES_NOT_EXIST,
+ CODE_SIP_SERVER_INTERNAL_ERROR,
+ CODE_SIP_SERVICE_UNAVAILABLE,
+ CODE_SIP_SERVER_TIMEOUT,
+ CODE_SIP_SERVER_ERROR,
+ CODE_SIP_USER_REJECTED,
+ CODE_SIP_GLOBAL_ERROR,
+ CODE_EMERGENCY_TEMP_FAILURE,
+ CODE_EMERGENCY_PERM_FAILURE,
+ CODE_SIP_USER_MARKED_UNWANTED,
+ CODE_SIP_METHOD_NOT_ALLOWED,
+ CODE_SIP_PROXY_AUTHENTICATION_REQUIRED,
+ CODE_SIP_REQUEST_ENTITY_TOO_LARGE,
+ CODE_SIP_REQUEST_URI_TOO_LARGE,
+ CODE_SIP_EXTENSION_REQUIRED,
+ CODE_SIP_INTERVAL_TOO_BRIEF,
+ CODE_SIP_CALL_OR_TRANS_DOES_NOT_EXIST,
+ CODE_SIP_LOOP_DETECTED,
+ CODE_SIP_TOO_MANY_HOPS,
+ CODE_SIP_AMBIGUOUS,
+ CODE_SIP_REQUEST_PENDING,
+ CODE_SIP_UNDECIPHERABLE,
+ CODE_MEDIA_INIT_FAILED,
+ CODE_MEDIA_NO_DATA,
+ CODE_MEDIA_NOT_ACCEPTABLE,
+ CODE_MEDIA_UNSPECIFIED,
+ CODE_USER_TERMINATED,
+ CODE_USER_NOANSWER,
+ CODE_USER_IGNORE,
+ CODE_USER_DECLINE,
+ CODE_LOW_BATTERY,
+ CODE_BLACKLISTED_CALL_ID,
+ CODE_USER_TERMINATED_BY_REMOTE,
+ CODE_USER_REJECTED_SESSION_MODIFICATION,
+ CODE_USER_CANCELLED_SESSION_MODIFICATION,
+ CODE_SESSION_MODIFICATION_FAILED,
+ CODE_UT_NOT_SUPPORTED,
+ CODE_UT_SERVICE_UNAVAILABLE,
+ CODE_UT_OPERATION_NOT_ALLOWED,
+ CODE_UT_NETWORK_ERROR,
+ CODE_UT_CB_PASSWORD_MISMATCH,
+ CODE_UT_SS_MODIFIED_TO_DIAL,
+ CODE_UT_SS_MODIFIED_TO_USSD,
+ CODE_UT_SS_MODIFIED_TO_SS,
+ CODE_UT_SS_MODIFIED_TO_DIAL_VIDEO,
+ CODE_ECBM_NOT_SUPPORTED,
+ CODE_MULTIENDPOINT_NOT_SUPPORTED,
+ CODE_REGISTRATION_ERROR,
+ CODE_ANSWERED_ELSEWHERE,
+ CODE_CALL_PULL_OUT_OF_SYNC,
+ CODE_CALL_END_CAUSE_CALL_PULL,
+ CODE_CALL_DROP_IWLAN_TO_LTE_UNAVAILABLE,
+ CODE_REJECTED_ELSEWHERE,
+ CODE_SUPP_SVC_FAILED,
+ CODE_SUPP_SVC_CANCELLED,
+ CODE_SUPP_SVC_REINVITE_COLLISION,
+ CODE_IWLAN_DPD_FAILURE,
+ CODE_EPDG_TUNNEL_ESTABLISH_FAILURE,
+ CODE_EPDG_TUNNEL_REKEY_FAILURE,
+ CODE_EPDG_TUNNEL_LOST_CONNECTION,
+ CODE_MAXIMUM_NUMBER_OF_CALLS_REACHED,
+ CODE_REMOTE_CALL_DECLINE,
+ CODE_DATA_LIMIT_REACHED,
+ CODE_DATA_DISABLED,
+ CODE_WIFI_LOST,
+ CODE_IKEV2_AUTH_FAILURE,
+ CODE_RADIO_OFF,
+ CODE_NO_VALID_SIM,
+ CODE_RADIO_INTERNAL_ERROR,
+ CODE_NETWORK_RESP_TIMEOUT,
+ CODE_NETWORK_REJECT,
+ CODE_RADIO_ACCESS_FAILURE,
+ CODE_RADIO_LINK_FAILURE,
+ CODE_RADIO_LINK_LOST,
+ CODE_RADIO_UPLINK_FAILURE,
+ CODE_RADIO_SETUP_FAILURE,
+ CODE_RADIO_RELEASE_NORMAL,
+ CODE_RADIO_RELEASE_ABNORMAL,
+ CODE_ACCESS_CLASS_BLOCKED,
+ CODE_NETWORK_DETACH,
+ CODE_SIP_ALTERNATE_EMERGENCY_CALL,
+ CODE_UNOBTAINABLE_NUMBER,
+ CODE_NO_CSFB_IN_CS_ROAM,
+ CODE_REJECT_UNKNOWN,
+ CODE_REJECT_ONGOING_CALL_WAITING_DISABLED,
+ CODE_REJECT_CALL_ON_OTHER_SUB,
+ CODE_REJECT_1X_COLLISION,
+ CODE_REJECT_SERVICE_NOT_REGISTERED,
+ CODE_REJECT_CALL_TYPE_NOT_ALLOWED,
+ CODE_REJECT_ONGOING_E911_CALL,
+ CODE_REJECT_ONGOING_CALL_SETUP,
+ CODE_REJECT_MAX_CALL_LIMIT_REACHED,
+ CODE_REJECT_UNSUPPORTED_SIP_HEADERS,
+ CODE_REJECT_UNSUPPORTED_SDP_HEADERS,
+ CODE_REJECT_ONGOING_CALL_TRANSFER,
+ CODE_REJECT_INTERNAL_ERROR,
+ CODE_REJECT_QOS_FAILURE,
+ CODE_REJECT_ONGOING_HANDOVER,
+ CODE_REJECT_VT_TTY_NOT_ALLOWED,
+ CODE_REJECT_ONGOING_CALL_UPGRADE,
+ CODE_REJECT_CONFERENCE_TTY_NOT_ALLOWED,
+ CODE_REJECT_ONGOING_CONFERENCE_CALL,
+ CODE_REJECT_VT_AVPF_NOT_ALLOWED,
+ CODE_REJECT_ONGOING_ENCRYPTED_CALL,
+ CODE_REJECT_ONGOING_CS_CALL,
+ CODE_OEM_CAUSE_1,
+ CODE_OEM_CAUSE_2,
+ CODE_OEM_CAUSE_3,
+ CODE_OEM_CAUSE_4,
+ CODE_OEM_CAUSE_5,
+ CODE_OEM_CAUSE_6,
+ CODE_OEM_CAUSE_7,
+ CODE_OEM_CAUSE_8,
+ CODE_OEM_CAUSE_9,
+ CODE_OEM_CAUSE_10,
+ CODE_OEM_CAUSE_11,
+ CODE_OEM_CAUSE_12,
+ CODE_OEM_CAUSE_13,
+ CODE_OEM_CAUSE_14,
+ CODE_OEM_CAUSE_15
+ }, prefix = "CODE_")
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ImsCode {}
+
+ /**
* Network string error messages.
* mExtraMessage may have these values.
*/
@@ -964,7 +1143,7 @@
/**
* @return an integer representing more information about the completion of an operation.
*/
- public int getCode() {
+ public @ImsCode int getCode() {
return mCode;
}
diff --git a/telephony/java/android/telephony/mbms/GroupCallCallback.java b/telephony/java/android/telephony/mbms/GroupCallCallback.java
index 77e36bb..603f4e6 100644
--- a/telephony/java/android/telephony/mbms/GroupCallCallback.java
+++ b/telephony/java/android/telephony/mbms/GroupCallCallback.java
@@ -57,7 +57,7 @@
* @param errorCode The error code.
* @param message A human-readable message generated by the middleware for debugging purposes.
*/
- void onError(@GroupCallError int errorCode, @Nullable String message);
+ default void onError(@GroupCallError int errorCode, @Nullable String message) {}
/**
* Called to indicate this call has changed state.
@@ -65,8 +65,8 @@
* See {@link GroupCall#STATE_STOPPED}, {@link GroupCall#STATE_STARTED}
* and {@link GroupCall#STATE_STALLED}.
*/
- void onGroupCallStateChanged(@GroupCall.GroupCallState int state,
- @GroupCall.GroupCallStateChangeReason int reason);
+ default void onGroupCallStateChanged(@GroupCall.GroupCallState int state,
+ @GroupCall.GroupCallStateChangeReason int reason) {}
/**
* Broadcast Signal Strength updated.
@@ -78,5 +78,6 @@
* {@link #SIGNAL_STRENGTH_UNAVAILABLE} if broadcast is not available
* for this call due to timing, geography or popularity.
*/
- void onBroadcastSignalStrengthUpdated(@IntRange(from = -1, to = 4) int signalStrength);
+ default void onBroadcastSignalStrengthUpdated(
+ @IntRange(from = -1, to = 4) int signalStrength) {}
}
diff --git a/telephony/java/android/telephony/mbms/MbmsGroupCallSessionCallback.java b/telephony/java/android/telephony/mbms/MbmsGroupCallSessionCallback.java
index 04e7ba1..ac7e172 100644
--- a/telephony/java/android/telephony/mbms/MbmsGroupCallSessionCallback.java
+++ b/telephony/java/android/telephony/mbms/MbmsGroupCallSessionCallback.java
@@ -57,7 +57,7 @@
* @param errorCode The error code.
* @param message A human-readable message generated by the middleware for debugging purposes.
*/
- void onError(@GroupCallError int errorCode, @Nullable String message);
+ default void onError(@GroupCallError int errorCode, @Nullable String message) {}
/**
* Indicates that the list of currently available SAIs has been updated. The app may use this
@@ -70,8 +70,8 @@
* @param availableSais A list of lists of available SAIS in neighboring cells, where each list
* contains the available SAIs in an individual cell.
*/
- void onAvailableSaisUpdated(@NonNull List<Integer> currentSais,
- @NonNull List<List<Integer>> availableSais);
+ default void onAvailableSaisUpdated(@NonNull List<Integer> currentSais,
+ @NonNull List<List<Integer>> availableSais) {}
/**
* Called soon after the app calls {@link MbmsGroupCallSession#create}. The information supplied
@@ -85,7 +85,7 @@
* @param interfaceName The interface name for the data link.
* @param index The index for the data link.
*/
- void onServiceInterfaceAvailable(@NonNull String interfaceName, int index);
+ default void onServiceInterfaceAvailable(@NonNull String interfaceName, int index) {}
/**
* Called to indicate that the middleware has been initialized and is ready.
@@ -95,5 +95,5 @@
* delivered via {@link #onError(int, String)} with error code
* {@link MbmsErrors.GeneralErrors#ERROR_MIDDLEWARE_NOT_YET_READY}.
*/
- void onMiddlewareReady();
+ default void onMiddlewareReady() {}
}
diff --git a/tests/RollbackTest/Android.mk b/tests/RollbackTest/Android.mk
index 780bb24..40d4eff 100644
--- a/tests/RollbackTest/Android.mk
+++ b/tests/RollbackTest/Android.mk
@@ -76,12 +76,14 @@
LOCAL_MODULE_TAGS := tests
LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
LOCAL_COMPATIBILITY_SUITE := general-tests
+LOCAL_COMPATIBILITY_SUPPORT_FILES := $(ROLLBACK_TEST_APEX_V1)
LOCAL_JAVA_RESOURCE_FILES := \
$(ROLLBACK_TEST_APP_AV1) \
$(ROLLBACK_TEST_APP_AV2) \
$(ROLLBACK_TEST_APP_A_CRASHING_V2) \
$(ROLLBACK_TEST_APP_BV1) \
- $(ROLLBACK_TEST_APP_BV2)
+ $(ROLLBACK_TEST_APP_BV2) \
+ $(ROLLBACK_TEST_APEX_V2)
LOCAL_SDK_VERSION := system_current
LOCAL_TEST_CONFIG := RollbackTest.xml
include $(BUILD_PACKAGE)
diff --git a/tests/RollbackTest/TestApex/Android.bp b/tests/RollbackTest/TestApex/Android.bp
new file mode 100644
index 0000000..a2a8e17
--- /dev/null
+++ b/tests/RollbackTest/TestApex/Android.bp
@@ -0,0 +1,56 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+apex {
+ name: "com.android.tests.rollback.testapex.RollbackTestApexV1",
+ manifest: "RollbackTestApexV1.json",
+ file_contexts: "apex.test",
+ prebuilts: ["RollbackTestApex.prebuilt.txt"],
+ key: "RollbackTestApex.key",
+ installable: false,
+}
+
+apex {
+ name: "com.android.tests.rollback.testapex.RollbackTestApexV2",
+ manifest: "RollbackTestApexV2.json",
+ file_contexts: "apex.test",
+ prebuilts: ["RollbackTestApex.prebuilt.txt"],
+ key: "RollbackTestApex.key",
+ installable: false,
+}
+
+apex_key {
+ name: "RollbackTestApex.key",
+ public_key: "com.android.tests.rollback.testapex.avbpubkey",
+ private_key: "com.android.tests.rollback.testapex.pem",
+ installable: false,
+}
+
+prebuilt_etc {
+ name: "RollbackTestApex.prebuilt.txt",
+ src: "RollbackTestApex.prebuilt.txt",
+}
+
+filegroup {
+ name: "RollbackTestApexV1_filegroup",
+ srcs: [":com.android.tests.rollback.testapex.RollbackTestApexV1"],
+ export_to_make_var: "ROLLBACK_TEST_APEX_V1",
+}
+
+filegroup {
+ name: "RollbackTestApexV2_filegroup",
+ srcs: [":com.android.tests.rollback.testapex.RollbackTestApexV2"],
+ export_to_make_var: "ROLLBACK_TEST_APEX_V2",
+}
+
diff --git a/tests/RollbackTest/TestApex/RollbackTestApex.prebuilt.txt b/tests/RollbackTest/TestApex/RollbackTestApex.prebuilt.txt
new file mode 100644
index 0000000..e871146
--- /dev/null
+++ b/tests/RollbackTest/TestApex/RollbackTestApex.prebuilt.txt
@@ -0,0 +1,3 @@
+
+This file contains dummy content to include in the RollbackTestApex.
+
diff --git a/tests/RollbackTest/TestApex/RollbackTestApexV1.json b/tests/RollbackTest/TestApex/RollbackTestApexV1.json
new file mode 100644
index 0000000..c3239ca
--- /dev/null
+++ b/tests/RollbackTest/TestApex/RollbackTestApexV1.json
@@ -0,0 +1,4 @@
+{
+ "name": "com.android.tests.rollback.testapex",
+ "version": 1
+}
diff --git a/tests/RollbackTest/TestApex/RollbackTestApexV2.json b/tests/RollbackTest/TestApex/RollbackTestApexV2.json
new file mode 100644
index 0000000..9de3f45
--- /dev/null
+++ b/tests/RollbackTest/TestApex/RollbackTestApexV2.json
@@ -0,0 +1,4 @@
+{
+ "name": "com.android.tests.rollback.testapex",
+ "version": 2
+}
diff --git a/tests/RollbackTest/TestApex/com.android.tests.rollback.testapex.avbpubkey b/tests/RollbackTest/TestApex/com.android.tests.rollback.testapex.avbpubkey
new file mode 100644
index 0000000..b347331
--- /dev/null
+++ b/tests/RollbackTest/TestApex/com.android.tests.rollback.testapex.avbpubkey
Binary files differ
diff --git a/tests/RollbackTest/TestApex/com.android.tests.rollback.testapex.pem b/tests/RollbackTest/TestApex/com.android.tests.rollback.testapex.pem
new file mode 100644
index 0000000..7181ce5
--- /dev/null
+++ b/tests/RollbackTest/TestApex/com.android.tests.rollback.testapex.pem
@@ -0,0 +1,51 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIJKQIBAAKCAgEArBLX+v6RMiK6soQFgbc0RZ+wticTD5sCbu9Q5B5WT7UnV1Wt
+cNI/n2bjks3UYNxTneOzMQOVjd4ln0BYZoNvoDtrc1BsYABpt4FywSq1zz/3sp3L
+3Vp8vaUyRsx0Da+PcOdYHPFs1WPX3Shw3MSPhUO/72KTm6GhO/cHEPEzSQLaw/zO
+8FiPpDdRqILqVJlu1DZ+i1DO+To9mKt59uLlxy3F+HAMnQaNW6+2NCV/vdCxQrEL
+m7PpsM6DymsFKcvVra1Il4apKkYNgcgTQ+AlmyPxGo6twfZvRMrU3bcrUmrg3OAx
+tMD7MufXlKkoFMlT5clrQ7P2ErKoFxhTVWhBL5ZJv/lKwaTfxCLyNvYQ7JALKtOL
+9xx4c77NdyYVjxMDAJ7E+Uj3QjdOv3FxM38Fcvt0zN4SSlmDjEDtm2e2w53Ztgz1
+zHAuNllIW1zGdPqa+ROoa9JLJePlyNXG9Rn1sSkjM9WwteaJ3nrdje6uBsvo7I9g
+1MWab2XNXQwJNHACICtyU7QPryGP4U/lyHqWEkdoXrs8O36gmTGU/EsJYSIVILax
+0HZC3zkmxHgiN95ZvD+Y5Y9HDXAN04kT8O1V/QD4QUCP8MGXNrkQP/JZAnu5Y2D1
+Wrn/7Tft2ON3OpH5vUgR99bKR549LcRrYgxeTnci3xkOsnTjZRxEUJFQgVUCAwEA
+AQKCAgEAlBzIMbDLk+cW4rAG+WeTo9ZXygKKQqV/i7OM4j5GtudMTL1fxDwFLZTn
+kCaBhzo+8ynaxPa71ViA87n0HZFHFRnwXFq+XkgctyrCdwjnY9RAxktS/l5z/t1i
+EFTOFDWod1t6mbcpoegGF8RGmZDLpL7zR/+G5LBUU4RHXcrwBQli+s3x5imkwoon
+TLAbOeSz5BBgDlTpQtdhy7bWDa+ybya0QCtagBLyvBfb8rjQYrduzOQOBODw5xJr
+mGFUGWztqUf9swfjNpMD71EjXApk9EwUrXJgmBMiJYmdfpa6wH9kvFpyDo8J6gBr
+rPeJm5LMF3+vR6Bw0Bld3DtBB8PsTrockOdeJNHfnY54480e7AahfM5gkeuIYC4P
+E3CIbyAgpS8+OLpsXP207AOjFz1TLiOqyDUDsBqSMNEQ7QbIPTQw8UZ/o0vEoqEv
+RrJAvIAv0KUcFNUL0t6VX2OXbV+i+T85wPey3XqK9P8Zzc8NUEGJ2paDUkSuemKC
+nF9/siGaoBHXsNUkQuV9xYo4co66MHDGAx/OdUM5lPiUcrF/bR+ZUA3p1BLUQXbQ
+aJD9uKlvJ4ZRSwhR/fQc5UiVWl3l76k+0Ia/Ddd1ArphVhbvGPOsm3W7wJ6KukSk
+rBJh1PPV1HnA+icS2Wvu2kVdz9+39cQUYJIICO1uwWfPIb/R5MUCggEBAOSwkwXs
+jqewNc6X4mc19RGKIMUvOrRrQkOOY33pI+BjoqI8cl+NOHNB7F8/gVnHIfhZpOG6
+WD5St1/qlSPT1Nte+38P+woAp5mdclZyRgYb4MjZybZihUPIPVgUbxiZTDHy1Sw0
+kRgmvaV3ccvG9/9bBUKNr9Z+XySsIhq5DzY+yIZ33vhLB/0g05x2HVIsSlsRWzri
+lw0iX/v07bdIRUIv7QB8xcLY/23pIbHAXS8DaknN8YxpKtjilOnUWVp/dx4SIcJY
+6PtSBfPcwVJ2MA/xXWBvDP82/XxvbxKnjj8lvkbfqMC6jrXJBw3NT/he8b932IBb
+PwLGU4hoKvjXfUcCggEBAMCfa4N1KUXW0Mo3BcDk2Hk8zVYuTPDNTWg1Mxb46sn+
+HNVJKU0OrejM2hNNCwzJG7eVQBorJj5XefhaQFTvWoLKbf1YZuWKaQrRwKkHEqDi
+edplA2RkpUebeS4KYIr22rzu3ZrZqiJmRU5kAS9hwOzwFvnXUgGy7IoZKXhpo0HK
+xvwylb/C5FXh97tecDdH/5evB/DC515TIhPzUQ5tpT0oAl9MU7Pe1E/opzrD/0sR
+dqKVJcl9vBRbtcAIkCOVpLoA9T2VvisafZAJaRXc3ACFMceewgpVAtBCsWUtLfvW
+fHMHWfti5je19SWg7bUK56uHJ0vdAI+irkfpe1t0aoMCggEAR5cPL3eSYOREs9vQ
+QEcf5NG82H2kfv5kzAkzFCN7267VJryNgWQQG+SzPk3/DD/OXpSRjShsn3X9ecVR
+0tlpdRMS3//8snDqBqjHNlCnoxnvEHE9OB83YLS6n2wmKykyNSCzoxcBpPHbxITT
+1tr+n626w87fEOKWnkBUnND59h1JYO79mfTDF3bDR+Oh4iuDS2bvjEuKxc3RBmry
+T8IMDGA8bT6iGhEcRSgKKD7z7NfA2kHiL/ZsN2EXBOw43J+yhnNephx3MtXGj0S4
+MDxXZ2ZDuQCKrQpl6CJqPwi8+v+xxTYW+d5s9nNsBeIT+sieHTZDTEtEOnYjiDwz
+15p92QKCAQEAkMwGNQawpOhLgYcFEzC0LcbwEFWzztx10N0U77LkRD16jTZ3Do73
+WmYLlLC4mr7e0A0o58MB96EodfHaJD7dSi5Dqkt25hw6xEBS1H0Vms1EjlCa0S/7
+Mq4D1QFF+5B/c8EX4ty20S8R8FCqt2SDc1kz3FHpOo+20kUB8Jtwdveox1J7UXB+
+1rSL1lSyhEviLbMMhAbvh+90UYz5pJ/1s9hMmDi3PyJFdWBNvZYyZcrV5He7tRCI
+fsFGCfol6CoIby5jLA1Rq/M46jq4vQ+ObfGyLv3/nWa0O7u2wHjK9WIRoSKomJmK
+t9xXURb9Obfd2Qo7FwMl9dNzsWkpKuGDYwKCAQB+hiWu2C+0foJ4Z8auM1SK8be4
+waplfD7qIvONE/vtl+VAN+eVpz5kkQJfXiafktAHahgEdSx43bofR0Kv0/R7IRs3
+M1WYAr0w+19TXLXkuh2oYIbcoGrIN3HMmlKMv44xh/QUhRe337cCLejP0SESNN+k
+5wT91SbJwuCw/QsG3sD6NIMtCNWdcsAQq/ruhz7pQ/JZUFOueV0tkzbK+oNHWbNU
+lS99qjPaVCXZFlz/t2/89cljh9mtRjcfXIBfuTijN9sXNcLTXsfLsyHJ86eEbI2U
+o2JQ7Sjs10LpiwBbNNHBmulARgRONNMgik6tpNIS0tk9eke0lCX42bDFtAD4
+-----END RSA PRIVATE KEY-----
diff --git a/tests/RollbackTest/src/com/android/tests/rollback/RollbackTest.java b/tests/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
index ace0e6d..4b277ae 100644
--- a/tests/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
+++ b/tests/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
@@ -37,7 +37,6 @@
import static org.junit.Assert.assertNull;
import static org.junit.Assert.fail;
-import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@@ -629,12 +628,9 @@
assertEquals(versionRolledBackTo, info.getVersionRolledBackTo().getLongVersionCode());
}
- // TODO: Allow installing test app along atomically with module metadata package so that
- // a failed test app will be flagged as a failed mainline app
/**
* Test bad update automatic rollback.
*/
- @Ignore
@Test
public void testBadUpdateRollback() throws Exception {
BroadcastReceiver crashCountReceiver = null;