Merge "Import translations. DO NOT MERGE"
diff --git a/Android.mk b/Android.mk
index 882f7a3..edbb94d 100644
--- a/Android.mk
+++ b/Android.mk
@@ -363,8 +363,8 @@
 LOCAL_SRC_GREYLIST := frameworks/base/config/hiddenapi-light-greylist.txt
 LOCAL_SRC_VENDOR_LIST := frameworks/base/config/hiddenapi-vendor-list.txt
 LOCAL_SRC_FORCE_BLACKLIST := frameworks/base/config/hiddenapi-force-blacklist.txt
-LOCAL_SRC_PUBLIC_API := $(INTERNAL_PLATFORM_DEX_API_FILE)
-LOCAL_SRC_PRIVATE_API := $(INTERNAL_PLATFORM_PRIVATE_DEX_API_FILE)
+LOCAL_SRC_PUBLIC_API := $(INTERNAL_PLATFORM_HIDDENAPI_PUBLIC_LIST)
+LOCAL_SRC_PRIVATE_API := $(INTERNAL_PLATFORM_HIDDENAPI_PRIVATE_LIST)
 LOCAL_SRC_REMOVED_API := $(INTERNAL_PLATFORM_REMOVED_DEX_API_FILE)
 
 LOCAL_SRC_ALL := \
diff --git a/apct-tests/perftests/core/src/android/os/BinderCallsStatsPerfTest.java b/apct-tests/perftests/core/src/android/os/BinderCallsStatsPerfTest.java
index 05293a2..66a2600d 100644
--- a/apct-tests/perftests/core/src/android/os/BinderCallsStatsPerfTest.java
+++ b/apct-tests/perftests/core/src/android/os/BinderCallsStatsPerfTest.java
@@ -48,7 +48,7 @@
 
     @Before
     public void setUp() {
-        mBinderCallsStats = new BinderCallsStats(new Random());
+        mBinderCallsStats = new BinderCallsStats(new BinderCallsStats.Injector());
     }
 
     @After
diff --git a/api/current.txt b/api/current.txt
index 7f4fae5..d70d1e7 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -15211,6 +15211,17 @@
     method public android.graphics.fonts.Font.Builder setWeight(int);
   }
 
+  public class FontFamily {
+    method public android.graphics.fonts.Font getFont(int);
+    method public int getFontCount();
+  }
+
+  public static class FontFamily.Builder {
+    ctor public FontFamily.Builder(android.graphics.fonts.Font);
+    method public android.graphics.fonts.FontFamily.Builder addFont(android.graphics.fonts.Font);
+    method public android.graphics.fonts.FontFamily build();
+  }
+
   public final class FontVariationAxis {
     ctor public FontVariationAxis(java.lang.String, float);
     method public static android.graphics.fonts.FontVariationAxis[] fromFontVariationSettings(java.lang.String);
diff --git a/api/system-current.txt b/api/system-current.txt
index 55664c5..01f4709 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -4667,6 +4667,7 @@
     field public static final android.os.Parcelable.Creator<android.service.notification.Adjustment> CREATOR;
     field public static final java.lang.String KEY_PEOPLE = "key_people";
     field public static final java.lang.String KEY_SMART_ACTIONS = "key_smart_actions";
+    field public static final java.lang.String KEY_SMART_REPLIES = "key_smart_replies";
     field public static final java.lang.String KEY_SNOOZE_CRITERIA = "key_snooze_criteria";
     field public static final java.lang.String KEY_USER_SENTIMENT = "key_user_sentiment";
   }
diff --git a/api/test-current.txt b/api/test-current.txt
index 462d22e..9dc61ee 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -314,9 +314,6 @@
   public final class SQLiteDebug {
     method public static void dump(android.util.Printer, java.lang.String[]);
     method public static android.database.sqlite.SQLiteDebug.PagerStats getDatabaseInfo();
-    field public static final boolean DEBUG_SQL_LOG;
-    field public static final boolean DEBUG_SQL_STATEMENTS;
-    field public static final boolean DEBUG_SQL_TIME;
   }
 
   public static class SQLiteDebug.DbStats {
@@ -1044,6 +1041,7 @@
     field public static final android.os.Parcelable.Creator<android.service.notification.Adjustment> CREATOR;
     field public static final java.lang.String KEY_PEOPLE = "key_people";
     field public static final java.lang.String KEY_SMART_ACTIONS = "key_smart_actions";
+    field public static final java.lang.String KEY_SMART_REPLIES = "key_smart_replies";
     field public static final java.lang.String KEY_SNOOZE_CRITERIA = "key_snooze_criteria";
     field public static final java.lang.String KEY_USER_SENTIMENT = "key_user_sentiment";
   }
diff --git a/config/hiddenapi-force-blacklist.txt b/config/hiddenapi-force-blacklist.txt
index 3a9e2d1..dca3b52 100644
--- a/config/hiddenapi-force-blacklist.txt
+++ b/config/hiddenapi-force-blacklist.txt
@@ -1,38 +1,38 @@
 Ldalvik/system/VMRuntime;->setHiddenApiExemptions([Ljava/lang/String;)V
 Ljava/lang/invoke/MethodHandles$Lookup;->IMPL_LOOKUP:Ljava/lang/invoke/MethodHandles$Lookup;
 Ljava/lang/invoke/VarHandle;->acquireFence()V
-Ljava/lang/invoke/VarHandle;->compareAndExchange([[Ljava/lang/Object;)Ljava/lang/Object;
-Ljava/lang/invoke/VarHandle;->compareAndExchangeAcquire([[Ljava/lang/Object;)Ljava/lang/Object;
-Ljava/lang/invoke/VarHandle;->compareAndExchangeRelease([[Ljava/lang/Object;)Ljava/lang/Object;
-Ljava/lang/invoke/VarHandle;->compareAndSet([[Ljava/lang/Object;)Z
+Ljava/lang/invoke/VarHandle;->compareAndExchange([Ljava/lang/Object;)Ljava/lang/Object;
+Ljava/lang/invoke/VarHandle;->compareAndExchangeAcquire([Ljava/lang/Object;)Ljava/lang/Object;
+Ljava/lang/invoke/VarHandle;->compareAndExchangeRelease([Ljava/lang/Object;)Ljava/lang/Object;
+Ljava/lang/invoke/VarHandle;->compareAndSet([Ljava/lang/Object;)Z
 Ljava/lang/invoke/VarHandle;->fullFence()V
-Ljava/lang/invoke/VarHandle;->get([[Ljava/lang/Object;)Ljava/lang/Object;
-Ljava/lang/invoke/VarHandle;->getAcquire([[Ljava/lang/Object;)Ljava/lang/Object;
-Ljava/lang/invoke/VarHandle;->getAndAdd([[Ljava/lang/Object;)Ljava/lang/Object;
-Ljava/lang/invoke/VarHandle;->getAndAddAcquire([[Ljava/lang/Object;)Ljava/lang/Object;
-Ljava/lang/invoke/VarHandle;->getAndAddRelease([[Ljava/lang/Object;)Ljava/lang/Object;
-Ljava/lang/invoke/VarHandle;->getAndBitwiseAnd([[Ljava/lang/Object;)Ljava/lang/Object;
-Ljava/lang/invoke/VarHandle;->getAndBitwiseAndAcquire([[Ljava/lang/Object;)Ljava/lang/Object;
-Ljava/lang/invoke/VarHandle;->getAndBitwiseAndRelease([[Ljava/lang/Object;)Ljava/lang/Object;
-Ljava/lang/invoke/VarHandle;->getAndBitwiseOr([[Ljava/lang/Object;)Ljava/lang/Object;
-Ljava/lang/invoke/VarHandle;->getAndBitwiseOrAcquire([[Ljava/lang/Object;)Ljava/lang/Object;
-Ljava/lang/invoke/VarHandle;->getAndBitwiseOrRelease([[Ljava/lang/Object;)Ljava/lang/Object;
-Ljava/lang/invoke/VarHandle;->getAndBitwiseXor([[Ljava/lang/Object;)Ljava/lang/Object;
-Ljava/lang/invoke/VarHandle;->getAndBitwiseXorAcquire([[Ljava/lang/Object;)Ljava/lang/Object;
-Ljava/lang/invoke/VarHandle;->getAndBitwiseXorRelease([[Ljava/lang/Object;)Ljava/lang/Object;
-Ljava/lang/invoke/VarHandle;->getAndSet([[Ljava/lang/Object;)Ljava/lang/Object;
-Ljava/lang/invoke/VarHandle;->getAndSetAcquire([[Ljava/lang/Object;)Ljava/lang/Object;
-Ljava/lang/invoke/VarHandle;->getAndSetRelease([[Ljava/lang/Object;)Ljava/lang/Object;
-Ljava/lang/invoke/VarHandle;->getOpaque([[Ljava/lang/Object;)Ljava/lang/Object;
-Ljava/lang/invoke/VarHandle;->getVolatile([[Ljava/lang/Object;)Ljava/lang/Object;
+Ljava/lang/invoke/VarHandle;->get([Ljava/lang/Object;)Ljava/lang/Object;
+Ljava/lang/invoke/VarHandle;->getAcquire([Ljava/lang/Object;)Ljava/lang/Object;
+Ljava/lang/invoke/VarHandle;->getAndAdd([Ljava/lang/Object;)Ljava/lang/Object;
+Ljava/lang/invoke/VarHandle;->getAndAddAcquire([Ljava/lang/Object;)Ljava/lang/Object;
+Ljava/lang/invoke/VarHandle;->getAndAddRelease([Ljava/lang/Object;)Ljava/lang/Object;
+Ljava/lang/invoke/VarHandle;->getAndBitwiseAnd([Ljava/lang/Object;)Ljava/lang/Object;
+Ljava/lang/invoke/VarHandle;->getAndBitwiseAndAcquire([Ljava/lang/Object;)Ljava/lang/Object;
+Ljava/lang/invoke/VarHandle;->getAndBitwiseAndRelease([Ljava/lang/Object;)Ljava/lang/Object;
+Ljava/lang/invoke/VarHandle;->getAndBitwiseOr([Ljava/lang/Object;)Ljava/lang/Object;
+Ljava/lang/invoke/VarHandle;->getAndBitwiseOrAcquire([Ljava/lang/Object;)Ljava/lang/Object;
+Ljava/lang/invoke/VarHandle;->getAndBitwiseOrRelease([Ljava/lang/Object;)Ljava/lang/Object;
+Ljava/lang/invoke/VarHandle;->getAndBitwiseXor([Ljava/lang/Object;)Ljava/lang/Object;
+Ljava/lang/invoke/VarHandle;->getAndBitwiseXorAcquire([Ljava/lang/Object;)Ljava/lang/Object;
+Ljava/lang/invoke/VarHandle;->getAndBitwiseXorRelease([Ljava/lang/Object;)Ljava/lang/Object;
+Ljava/lang/invoke/VarHandle;->getAndSet([Ljava/lang/Object;)Ljava/lang/Object;
+Ljava/lang/invoke/VarHandle;->getAndSetAcquire([Ljava/lang/Object;)Ljava/lang/Object;
+Ljava/lang/invoke/VarHandle;->getAndSetRelease([Ljava/lang/Object;)Ljava/lang/Object;
+Ljava/lang/invoke/VarHandle;->getOpaque([Ljava/lang/Object;)Ljava/lang/Object;
+Ljava/lang/invoke/VarHandle;->getVolatile([Ljava/lang/Object;)Ljava/lang/Object;
 Ljava/lang/invoke/VarHandle;->loadLoadFence()V
 Ljava/lang/invoke/VarHandle;->releaseFence()V
-Ljava/lang/invoke/VarHandle;->set([[Ljava/lang/Object;)V
-Ljava/lang/invoke/VarHandle;->setOpaque([[Ljava/lang/Object;)V
-Ljava/lang/invoke/VarHandle;->setRelease([[Ljava/lang/Object;)V
-Ljava/lang/invoke/VarHandle;->setVolatile([[Ljava/lang/Object;)V
+Ljava/lang/invoke/VarHandle;->set([Ljava/lang/Object;)V
+Ljava/lang/invoke/VarHandle;->setOpaque([Ljava/lang/Object;)V
+Ljava/lang/invoke/VarHandle;->setRelease([Ljava/lang/Object;)V
+Ljava/lang/invoke/VarHandle;->setVolatile([Ljava/lang/Object;)V
 Ljava/lang/invoke/VarHandle;->storeStoreFence()V
-Ljava/lang/invoke/VarHandle;->weakCompareAndSet([[Ljava/lang/Object;)Z
-Ljava/lang/invoke/VarHandle;->weakCompareAndSetAcquire([[Ljava/lang/Object;)Z
-Ljava/lang/invoke/VarHandle;->weakCompareAndSetPlain([[Ljava/lang/Object;)Z
-Ljava/lang/invoke/VarHandle;->weakCompareAndSetRelease([[Ljava/lang/Object;)Z
+Ljava/lang/invoke/VarHandle;->weakCompareAndSet([Ljava/lang/Object;)Z
+Ljava/lang/invoke/VarHandle;->weakCompareAndSetAcquire([Ljava/lang/Object;)Z
+Ljava/lang/invoke/VarHandle;->weakCompareAndSetPlain([Ljava/lang/Object;)Z
+Ljava/lang/invoke/VarHandle;->weakCompareAndSetRelease([Ljava/lang/Object;)Z
diff --git a/config/hiddenapi-light-greylist.txt b/config/hiddenapi-light-greylist.txt
index 588f544..3fdf1b4 100644
--- a/config/hiddenapi-light-greylist.txt
+++ b/config/hiddenapi-light-greylist.txt
@@ -1,6 +1,3 @@
-Landroid/accessibilityservice/AccessibilityService;->mInfo:Landroid/accessibilityservice/AccessibilityServiceInfo;
-Landroid/accessibilityservice/AccessibilityService;->mWindowToken:Landroid/os/IBinder;
-Landroid/accessibilityservice/AccessibilityServiceInfo;->setCapabilities(I)V
 Landroid/accessibilityservice/IAccessibilityServiceConnection$Stub;-><init>()V
 Landroid/accessibilityservice/IAccessibilityServiceConnection$Stub;->asInterface(Landroid/os/IBinder;)Landroid/accessibilityservice/IAccessibilityServiceConnection;
 Landroid/accounts/Account;->accessId:Ljava/lang/String;
@@ -51,13 +48,6 @@
 Landroid/accounts/IAccountManagerResponse$Stub;->asInterface(Landroid/os/IBinder;)Landroid/accounts/IAccountManagerResponse;
 Landroid/accounts/IAccountManagerResponse;->onError(ILjava/lang/String;)V
 Landroid/accounts/IAccountManagerResponse;->onResult(Landroid/os/Bundle;)V
-Landroid/animation/Animator;->reverse()V
-Landroid/animation/ArgbEvaluator;->getInstance()Landroid/animation/ArgbEvaluator;
-Landroid/animation/LayoutTransition;->cancel()V
-Landroid/animation/LayoutTransition;->cancel(I)V
-Landroid/animation/ValueAnimator;->animateValue(F)V
-Landroid/animation/ValueAnimator;->mDuration:J
-Landroid/animation/ValueAnimator;->sDurationScale:F
 Landroid/app/ActionBar;->collapseActionView()Z
 Landroid/app/ActionBar;->DISPLAY_TITLE_MULTIPLE_LINES:I
 Landroid/app/ActionBar;->setShowHideAnimationEnabled(Z)V
@@ -533,7 +523,7 @@
 Landroid/app/DownloadManager$Request;->mUri:Landroid/net/Uri;
 Landroid/app/DownloadManager;->getWhereArgsForIds([J)[Ljava/lang/String;
 Landroid/app/DownloadManager;->getWhereClauseForIds([J)Ljava/lang/String;
-Landroid/app/DownloadManager;->restartDownload([[J)V
+Landroid/app/DownloadManager;->restartDownload([J)V
 Landroid/app/DownloadManager;->setAccessAllDownloads(Z)V
 Landroid/app/DownloadManager;->setAccessFilename(Z)V
 Landroid/app/DownloadManager;->UNDERLYING_COLUMNS:[Ljava/lang/String;
@@ -1049,7 +1039,6 @@
 Landroid/bluetooth/BluetoothClass;->doesClassMatch(I)Z
 Landroid/bluetooth/BluetoothClass;->PROFILE_A2DP:I
 Landroid/bluetooth/BluetoothClass;->PROFILE_HEADSET:I
-Landroid/bluetooth/BluetoothCodecConfig;
 Landroid/bluetooth/BluetoothCodecConfig;-><init>(IIIIIJJJJ)V
 Landroid/bluetooth/BluetoothCodecConfig;->BITS_PER_SAMPLE_16:I
 Landroid/bluetooth/BluetoothCodecConfig;->BITS_PER_SAMPLE_24:I
@@ -1085,7 +1074,6 @@
 Landroid/bluetooth/BluetoothCodecConfig;->SOURCE_CODEC_TYPE_LDAC:I
 Landroid/bluetooth/BluetoothCodecConfig;->SOURCE_CODEC_TYPE_MAX:I
 Landroid/bluetooth/BluetoothCodecConfig;->SOURCE_CODEC_TYPE_SBC:I
-Landroid/bluetooth/BluetoothCodecStatus;
 Landroid/bluetooth/BluetoothCodecStatus;->EXTRA_CODEC_STATUS:Ljava/lang/String;
 Landroid/bluetooth/BluetoothCodecStatus;->getCodecConfig()Landroid/bluetooth/BluetoothCodecConfig;
 Landroid/bluetooth/BluetoothCodecStatus;->getCodecsLocalCapabilities()[Landroid/bluetooth/BluetoothCodecConfig;
@@ -1453,10 +1441,15 @@
 Landroid/content/pm/ComponentInfo;->getComponentName()Landroid/content/ComponentName;
 Landroid/content/pm/IPackageDataObserver$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
 Landroid/content/pm/IPackageDataObserver$Stub$Proxy;->mRemote:Landroid/os/IBinder;
+Landroid/content/pm/IPackageDataObserver$Stub$Proxy;->onRemoveCompleted(Ljava/lang/String;Z)V
 Landroid/content/pm/IPackageDataObserver$Stub;->asInterface(Landroid/os/IBinder;)Landroid/content/pm/IPackageDataObserver;
+Landroid/content/pm/IPackageDataObserver$Stub;->DESCRIPTOR:Ljava/lang/String;
+Landroid/content/pm/IPackageDataObserver$Stub;->TRANSACTION_onRemoveCompleted:I
 Landroid/content/pm/IPackageDataObserver;->onRemoveCompleted(Ljava/lang/String;Z)V
 Landroid/content/pm/IPackageDeleteObserver$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
 Landroid/content/pm/IPackageDeleteObserver$Stub;->asInterface(Landroid/os/IBinder;)Landroid/content/pm/IPackageDeleteObserver;
+Landroid/content/pm/IPackageDeleteObserver$Stub;->DESCRIPTOR:Ljava/lang/String;
+Landroid/content/pm/IPackageDeleteObserver$Stub;->TRANSACTION_packageDeleted:I
 Landroid/content/pm/IPackageDeleteObserver2$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
 Landroid/content/pm/IPackageDeleteObserver2$Stub$Proxy;->mRemote:Landroid/os/IBinder;
 Landroid/content/pm/IPackageDeleteObserver2$Stub;-><init>()V
@@ -1555,6 +1548,8 @@
 Landroid/content/pm/IPackageStatsObserver$Stub$Proxy;->mRemote:Landroid/os/IBinder;
 Landroid/content/pm/IPackageStatsObserver$Stub;-><init>()V
 Landroid/content/pm/IPackageStatsObserver$Stub;->asInterface(Landroid/os/IBinder;)Landroid/content/pm/IPackageStatsObserver;
+Landroid/content/pm/IPackageStatsObserver$Stub;->DESCRIPTOR:Ljava/lang/String;
+Landroid/content/pm/IPackageStatsObserver$Stub;->TRANSACTION_onGetStatsCompleted:I
 Landroid/content/pm/IShortcutService$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
 Landroid/content/pm/IShortcutService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/content/pm/IShortcutService;
 Landroid/content/pm/LauncherActivityInfo;->mActivityInfo:Landroid/content/pm/ActivityInfo;
@@ -2091,41 +2086,7 @@
 Landroid/filterfw/GraphEnvironment;-><init>()V
 Landroid/filterfw/GraphEnvironment;->getRunner(II)Landroid/filterfw/core/GraphRunner;
 Landroid/filterfw/GraphEnvironment;->loadGraph(Landroid/content/Context;I)I
-Landroid/hardware/biometrics/BiometricConstants;->BIOMETRIC_ACQUIRED_GOOD:I
-Landroid/hardware/biometrics/BiometricConstants;->BIOMETRIC_ACQUIRED_IMAGER_DIRTY:I
-Landroid/hardware/biometrics/BiometricConstants;->BIOMETRIC_ACQUIRED_INSUFFICIENT:I
-Landroid/hardware/biometrics/BiometricConstants;->BIOMETRIC_ACQUIRED_PARTIAL:I
-Landroid/hardware/biometrics/BiometricConstants;->BIOMETRIC_ACQUIRED_TOO_FAST:I
-Landroid/hardware/biometrics/BiometricConstants;->BIOMETRIC_ACQUIRED_TOO_SLOW:I
-Landroid/hardware/biometrics/BiometricConstants;->BIOMETRIC_ERROR_CANCELED:I
-Landroid/hardware/biometrics/BiometricConstants;->BIOMETRIC_ERROR_HW_NOT_PRESENT:I
-Landroid/hardware/biometrics/BiometricConstants;->BIOMETRIC_ERROR_HW_UNAVAILABLE:I
-Landroid/hardware/biometrics/BiometricConstants;->BIOMETRIC_ERROR_LOCKOUT:I
-Landroid/hardware/biometrics/BiometricConstants;->BIOMETRIC_ERROR_LOCKOUT_PERMANENT:I
-Landroid/hardware/biometrics/BiometricConstants;->BIOMETRIC_ERROR_NO_BIOMETRICS:I
-Landroid/hardware/biometrics/BiometricConstants;->BIOMETRIC_ERROR_NO_SPACE:I
-Landroid/hardware/biometrics/BiometricConstants;->BIOMETRIC_ERROR_TIMEOUT:I
-Landroid/hardware/biometrics/BiometricConstants;->BIOMETRIC_ERROR_UNABLE_TO_PROCESS:I
-Landroid/hardware/biometrics/BiometricConstants;->BIOMETRIC_ERROR_USER_CANCELED:I
-Landroid/hardware/biometrics/BiometricConstants;->BIOMETRIC_ERROR_VENDOR:I
 Landroid/hardware/biometrics/BiometricConstants;->BIOMETRIC_ERROR_VENDOR_BASE:I
-Landroid/hardware/biometrics/BiometricFingerprintConstants;->FINGERPRINT_ACQUIRED_GOOD:I
-Landroid/hardware/biometrics/BiometricFingerprintConstants;->FINGERPRINT_ACQUIRED_IMAGER_DIRTY:I
-Landroid/hardware/biometrics/BiometricFingerprintConstants;->FINGERPRINT_ACQUIRED_INSUFFICIENT:I
-Landroid/hardware/biometrics/BiometricFingerprintConstants;->FINGERPRINT_ACQUIRED_PARTIAL:I
-Landroid/hardware/biometrics/BiometricFingerprintConstants;->FINGERPRINT_ACQUIRED_TOO_FAST:I
-Landroid/hardware/biometrics/BiometricFingerprintConstants;->FINGERPRINT_ACQUIRED_TOO_SLOW:I
-Landroid/hardware/biometrics/BiometricFingerprintConstants;->FINGERPRINT_ERROR_CANCELED:I
-Landroid/hardware/biometrics/BiometricFingerprintConstants;->FINGERPRINT_ERROR_HW_NOT_PRESENT:I
-Landroid/hardware/biometrics/BiometricFingerprintConstants;->FINGERPRINT_ERROR_HW_UNAVAILABLE:I
-Landroid/hardware/biometrics/BiometricFingerprintConstants;->FINGERPRINT_ERROR_LOCKOUT:I
-Landroid/hardware/biometrics/BiometricFingerprintConstants;->FINGERPRINT_ERROR_LOCKOUT_PERMANENT:I
-Landroid/hardware/biometrics/BiometricFingerprintConstants;->FINGERPRINT_ERROR_NO_FINGERPRINTS:I
-Landroid/hardware/biometrics/BiometricFingerprintConstants;->FINGERPRINT_ERROR_NO_SPACE:I
-Landroid/hardware/biometrics/BiometricFingerprintConstants;->FINGERPRINT_ERROR_TIMEOUT:I
-Landroid/hardware/biometrics/BiometricFingerprintConstants;->FINGERPRINT_ERROR_UNABLE_TO_PROCESS:I
-Landroid/hardware/biometrics/BiometricFingerprintConstants;->FINGERPRINT_ERROR_USER_CANCELED:I
-Landroid/hardware/biometrics/BiometricFingerprintConstants;->FINGERPRINT_ERROR_VENDOR:I
 Landroid/hardware/biometrics/BiometricFingerprintConstants;->FINGERPRINT_ERROR_VENDOR_BASE:I
 Landroid/hardware/Camera$Parameters;->copyFrom(Landroid/hardware/Camera$Parameters;)V
 Landroid/hardware/Camera$Parameters;->dump()V
@@ -2386,7 +2347,6 @@
 Landroid/icu/text/Transliterator;->transliterate(Landroid/icu/text/Replaceable;Landroid/icu/text/Transliterator$Position;Ljava/lang/String;)V
 Landroid/icu/text/Transliterator;->transliterate(Ljava/lang/String;)Ljava/lang/String;
 Landroid/icu/text/UFormat;->getLocale(Landroid/icu/util/ULocale$Type;)Landroid/icu/util/ULocale;
-Landroid/icu/text/UForwardCharacterIterator;->DONE:I
 Landroid/icu/util/Calendar;->getLocale(Landroid/icu/util/ULocale$Type;)Landroid/icu/util/ULocale;
 Landroid/icu/util/PersianCalendar;-><init>(Ljava/util/Locale;)V
 Landroid/icu/util/UResourceBundle;->getBundleInstance(Ljava/lang/String;Landroid/icu/util/ULocale;)Landroid/icu/util/UResourceBundle;
@@ -2395,26 +2355,6 @@
 Landroid/icu/util/UResourceBundle;->getType()I
 Landroid/icu/util/UResourceBundleIterator;->hasNext()Z
 Landroid/icu/util/UResourceBundleIterator;->next()Landroid/icu/util/UResourceBundle;
-Landroid/inputmethodservice/InputMethodService$SettingsObserver;->shouldShowImeWithHardKeyboard()Z
-Landroid/inputmethodservice/InputMethodService;->mExtractEditText:Landroid/inputmethodservice/ExtractEditText;
-Landroid/inputmethodservice/InputMethodService;->mExtractView:Landroid/view/View;
-Landroid/inputmethodservice/InputMethodService;->mRootView:Landroid/view/View;
-Landroid/inputmethodservice/InputMethodService;->mSettingsObserver:Landroid/inputmethodservice/InputMethodService$SettingsObserver;
-Landroid/inputmethodservice/InputMethodService;->mTheme:I
-Landroid/inputmethodservice/InputMethodService;->mTmpInsets:Landroid/inputmethodservice/InputMethodService$Insets;
-Landroid/inputmethodservice/InputMethodService;->onExtractedDeleteText(II)V
-Landroid/inputmethodservice/InputMethodService;->onExtractedReplaceText(IILjava/lang/CharSequence;)V
-Landroid/inputmethodservice/InputMethodService;->onExtractedSetSpan(Ljava/lang/Object;III)V
-Landroid/inputmethodservice/Keyboard;->mModifierKeys:Ljava/util/List;
-Landroid/inputmethodservice/Keyboard;->mTotalHeight:I
-Landroid/inputmethodservice/Keyboard;->mTotalWidth:I
-Landroid/inputmethodservice/Keyboard;->resize(II)V
-Landroid/inputmethodservice/KeyboardView;->mKeyBackground:Landroid/graphics/drawable/Drawable;
-Landroid/inputmethodservice/KeyboardView;->mLabelTextSize:I
-Landroid/inputmethodservice/KeyboardView;->mPreviewText:Landroid/widget/TextView;
-Landroid/inputmethodservice/KeyboardView;->openPopupIfRequired(Landroid/view/MotionEvent;)Z
-Landroid/inputmethodservice/KeyboardView;->repeatKey()Z
-Landroid/inputmethodservice/KeyboardView;->showKey(I)V
 Landroid/location/Country;-><init>(Ljava/lang/String;I)V
 Landroid/location/Country;->getCountryIso()Ljava/lang/String;
 Landroid/location/Country;->getSource()I
@@ -2914,81 +2854,6 @@
 Landroid/media/TimedText;->getObject(I)Ljava/lang/Object;
 Landroid/media/ToneGenerator;->mNativeContext:J
 Landroid/media/TtmlRenderer;-><init>(Landroid/content/Context;)V
-Landroid/media/tv/TvContract$PreviewProgramColumns;->ASPECT_RATIO_16_9:I
-Landroid/media/tv/TvContract$PreviewProgramColumns;->ASPECT_RATIO_1_1:I
-Landroid/media/tv/TvContract$PreviewProgramColumns;->ASPECT_RATIO_2_3:I
-Landroid/media/tv/TvContract$PreviewProgramColumns;->ASPECT_RATIO_3_2:I
-Landroid/media/tv/TvContract$PreviewProgramColumns;->ASPECT_RATIO_4_3:I
-Landroid/media/tv/TvContract$PreviewProgramColumns;->AVAILABILITY_AVAILABLE:I
-Landroid/media/tv/TvContract$PreviewProgramColumns;->AVAILABILITY_FREE_WITH_SUBSCRIPTION:I
-Landroid/media/tv/TvContract$PreviewProgramColumns;->AVAILABILITY_PAID_CONTENT:I
-Landroid/media/tv/TvContract$PreviewProgramColumns;->COLUMN_AUTHOR:Ljava/lang/String;
-Landroid/media/tv/TvContract$PreviewProgramColumns;->COLUMN_AVAILABILITY:Ljava/lang/String;
-Landroid/media/tv/TvContract$PreviewProgramColumns;->COLUMN_BROWSABLE:Ljava/lang/String;
-Landroid/media/tv/TvContract$PreviewProgramColumns;->COLUMN_CONTENT_ID:Ljava/lang/String;
-Landroid/media/tv/TvContract$PreviewProgramColumns;->COLUMN_DURATION_MILLIS:Ljava/lang/String;
-Landroid/media/tv/TvContract$PreviewProgramColumns;->COLUMN_INTENT_URI:Ljava/lang/String;
-Landroid/media/tv/TvContract$PreviewProgramColumns;->COLUMN_INTERACTION_COUNT:Ljava/lang/String;
-Landroid/media/tv/TvContract$PreviewProgramColumns;->COLUMN_INTERACTION_TYPE:Ljava/lang/String;
-Landroid/media/tv/TvContract$PreviewProgramColumns;->COLUMN_INTERNAL_PROVIDER_ID:Ljava/lang/String;
-Landroid/media/tv/TvContract$PreviewProgramColumns;->COLUMN_ITEM_COUNT:Ljava/lang/String;
-Landroid/media/tv/TvContract$PreviewProgramColumns;->COLUMN_LAST_PLAYBACK_POSITION_MILLIS:Ljava/lang/String;
-Landroid/media/tv/TvContract$PreviewProgramColumns;->COLUMN_LIVE:Ljava/lang/String;
-Landroid/media/tv/TvContract$PreviewProgramColumns;->COLUMN_LOGO_URI:Ljava/lang/String;
-Landroid/media/tv/TvContract$PreviewProgramColumns;->COLUMN_OFFER_PRICE:Ljava/lang/String;
-Landroid/media/tv/TvContract$PreviewProgramColumns;->COLUMN_POSTER_ART_ASPECT_RATIO:Ljava/lang/String;
-Landroid/media/tv/TvContract$PreviewProgramColumns;->COLUMN_PREVIEW_VIDEO_URI:Ljava/lang/String;
-Landroid/media/tv/TvContract$PreviewProgramColumns;->COLUMN_RELEASE_DATE:Ljava/lang/String;
-Landroid/media/tv/TvContract$PreviewProgramColumns;->COLUMN_STARTING_PRICE:Ljava/lang/String;
-Landroid/media/tv/TvContract$PreviewProgramColumns;->COLUMN_THUMBNAIL_ASPECT_RATIO:Ljava/lang/String;
-Landroid/media/tv/TvContract$PreviewProgramColumns;->COLUMN_TRANSIENT:Ljava/lang/String;
-Landroid/media/tv/TvContract$PreviewProgramColumns;->COLUMN_TYPE:Ljava/lang/String;
-Landroid/media/tv/TvContract$PreviewProgramColumns;->INTERACTION_TYPE_FANS:I
-Landroid/media/tv/TvContract$PreviewProgramColumns;->INTERACTION_TYPE_FOLLOWERS:I
-Landroid/media/tv/TvContract$PreviewProgramColumns;->INTERACTION_TYPE_LIKES:I
-Landroid/media/tv/TvContract$PreviewProgramColumns;->INTERACTION_TYPE_LISTENS:I
-Landroid/media/tv/TvContract$PreviewProgramColumns;->INTERACTION_TYPE_THUMBS:I
-Landroid/media/tv/TvContract$PreviewProgramColumns;->INTERACTION_TYPE_VIEWERS:I
-Landroid/media/tv/TvContract$PreviewProgramColumns;->INTERACTION_TYPE_VIEWS:I
-Landroid/media/tv/TvContract$PreviewProgramColumns;->TYPE_ALBUM:I
-Landroid/media/tv/TvContract$PreviewProgramColumns;->TYPE_ARTIST:I
-Landroid/media/tv/TvContract$PreviewProgramColumns;->TYPE_CHANNEL:I
-Landroid/media/tv/TvContract$PreviewProgramColumns;->TYPE_CLIP:I
-Landroid/media/tv/TvContract$PreviewProgramColumns;->TYPE_EVENT:I
-Landroid/media/tv/TvContract$PreviewProgramColumns;->TYPE_MOVIE:I
-Landroid/media/tv/TvContract$PreviewProgramColumns;->TYPE_PLAYLIST:I
-Landroid/media/tv/TvContract$PreviewProgramColumns;->TYPE_STATION:I
-Landroid/media/tv/TvContract$PreviewProgramColumns;->TYPE_TRACK:I
-Landroid/media/tv/TvContract$PreviewProgramColumns;->TYPE_TV_EPISODE:I
-Landroid/media/tv/TvContract$PreviewProgramColumns;->TYPE_TV_SEASON:I
-Landroid/media/tv/TvContract$PreviewProgramColumns;->TYPE_TV_SERIES:I
-Landroid/media/tv/TvContract$ProgramColumns;->COLUMN_AUDIO_LANGUAGE:Ljava/lang/String;
-Landroid/media/tv/TvContract$ProgramColumns;->COLUMN_CANONICAL_GENRE:Ljava/lang/String;
-Landroid/media/tv/TvContract$ProgramColumns;->COLUMN_CONTENT_RATING:Ljava/lang/String;
-Landroid/media/tv/TvContract$ProgramColumns;->COLUMN_EPISODE_DISPLAY_NUMBER:Ljava/lang/String;
-Landroid/media/tv/TvContract$ProgramColumns;->COLUMN_EPISODE_TITLE:Ljava/lang/String;
-Landroid/media/tv/TvContract$ProgramColumns;->COLUMN_INTERNAL_PROVIDER_DATA:Ljava/lang/String;
-Landroid/media/tv/TvContract$ProgramColumns;->COLUMN_INTERNAL_PROVIDER_FLAG1:Ljava/lang/String;
-Landroid/media/tv/TvContract$ProgramColumns;->COLUMN_INTERNAL_PROVIDER_FLAG2:Ljava/lang/String;
-Landroid/media/tv/TvContract$ProgramColumns;->COLUMN_INTERNAL_PROVIDER_FLAG3:Ljava/lang/String;
-Landroid/media/tv/TvContract$ProgramColumns;->COLUMN_INTERNAL_PROVIDER_FLAG4:Ljava/lang/String;
-Landroid/media/tv/TvContract$ProgramColumns;->COLUMN_LONG_DESCRIPTION:Ljava/lang/String;
-Landroid/media/tv/TvContract$ProgramColumns;->COLUMN_POSTER_ART_URI:Ljava/lang/String;
-Landroid/media/tv/TvContract$ProgramColumns;->COLUMN_REVIEW_RATING:Ljava/lang/String;
-Landroid/media/tv/TvContract$ProgramColumns;->COLUMN_REVIEW_RATING_STYLE:Ljava/lang/String;
-Landroid/media/tv/TvContract$ProgramColumns;->COLUMN_SEARCHABLE:Ljava/lang/String;
-Landroid/media/tv/TvContract$ProgramColumns;->COLUMN_SEASON_DISPLAY_NUMBER:Ljava/lang/String;
-Landroid/media/tv/TvContract$ProgramColumns;->COLUMN_SEASON_TITLE:Ljava/lang/String;
-Landroid/media/tv/TvContract$ProgramColumns;->COLUMN_SERIES_ID:Ljava/lang/String;
-Landroid/media/tv/TvContract$ProgramColumns;->COLUMN_SHORT_DESCRIPTION:Ljava/lang/String;
-Landroid/media/tv/TvContract$ProgramColumns;->COLUMN_THUMBNAIL_URI:Ljava/lang/String;
-Landroid/media/tv/TvContract$ProgramColumns;->COLUMN_TITLE:Ljava/lang/String;
-Landroid/media/tv/TvContract$ProgramColumns;->COLUMN_VERSION_NUMBER:Ljava/lang/String;
-Landroid/media/tv/TvContract$ProgramColumns;->COLUMN_VIDEO_HEIGHT:Ljava/lang/String;
-Landroid/media/tv/TvContract$ProgramColumns;->COLUMN_VIDEO_WIDTH:Ljava/lang/String;
-Landroid/media/tv/TvContract$ProgramColumns;->REVIEW_RATING_STYLE_PERCENTAGE:I
-Landroid/media/tv/TvContract$ProgramColumns;->REVIEW_RATING_STYLE_STARS:I
-Landroid/media/tv/TvContract$ProgramColumns;->REVIEW_RATING_STYLE_THUMBS_UP_DOWN:I
 Landroid/media/tv/TvInputInfo;->getComponent()Landroid/content/ComponentName;
 Landroid/media/tv/TvInputService$Session;->mOverlayFrame:Landroid/graphics/Rect;
 Landroid/media/VolumeShaper$Configuration;-><init>(IIIDI[F[F)V
@@ -3480,28 +3345,6 @@
 Landroid/net/wifi/WifiSsid;->getOctets()[B
 Landroid/net/wifi/WifiSsid;->NONE:Ljava/lang/String;
 Landroid/net/wifi/WifiSsid;->octets:Ljava/io/ByteArrayOutputStream;
-Landroid/nfc/cardemulation/AidGroup;-><init>(Ljava/lang/String;Ljava/lang/String;)V
-Landroid/nfc/cardemulation/AidGroup;->aids:Ljava/util/List;
-Landroid/nfc/cardemulation/AidGroup;->category:Ljava/lang/String;
-Landroid/nfc/cardemulation/AidGroup;->createFromXml(Lorg/xmlpull/v1/XmlPullParser;)Landroid/nfc/cardemulation/AidGroup;
-Landroid/nfc/cardemulation/AidGroup;->CREATOR:Landroid/os/Parcelable$Creator;
-Landroid/nfc/cardemulation/AidGroup;->description:Ljava/lang/String;
-Landroid/nfc/cardemulation/AidGroup;->getAids()Ljava/util/List;
-Landroid/nfc/cardemulation/AidGroup;->getCategory()Ljava/lang/String;
-Landroid/nfc/cardemulation/AidGroup;->writeAsXml(Lorg/xmlpull/v1/XmlSerializer;)V
-Landroid/nfc/cardemulation/ApduServiceInfo;-><init>(Landroid/content/pm/PackageManager;Landroid/content/pm/ResolveInfo;Z)V
-Landroid/nfc/cardemulation/ApduServiceInfo;-><init>(Landroid/content/pm/ResolveInfo;ZLjava/lang/String;Ljava/util/ArrayList;Ljava/util/ArrayList;ZIILjava/lang/String;)V
-Landroid/nfc/cardemulation/ApduServiceInfo;->CREATOR:Landroid/os/Parcelable$Creator;
-Landroid/nfc/cardemulation/ApduServiceInfo;->getDescription()Ljava/lang/String;
-Landroid/nfc/cardemulation/ApduServiceInfo;->getSettingsActivityName()Ljava/lang/String;
-Landroid/nfc/cardemulation/ApduServiceInfo;->getUid()I
-Landroid/nfc/cardemulation/ApduServiceInfo;->isOnHost()Z
-Landroid/nfc/cardemulation/ApduServiceInfo;->loadBanner(Landroid/content/pm/PackageManager;)Landroid/graphics/drawable/Drawable;
-Landroid/nfc/cardemulation/ApduServiceInfo;->mDynamicAidGroups:Ljava/util/HashMap;
-Landroid/nfc/cardemulation/ApduServiceInfo;->mService:Landroid/content/pm/ResolveInfo;
-Landroid/nfc/cardemulation/ApduServiceInfo;->mStaticAidGroups:Ljava/util/HashMap;
-Landroid/nfc/cardemulation/ApduServiceInfo;->requiresUnlock()Z
-Landroid/nfc/ErrorCodes;->isError(I)Z
 Landroid/nfc/INfcAdapter$Stub;->TRANSACTION_enable:I
 Landroid/nfc/INfcAdapterExtras;->authenticate(Ljava/lang/String;[B)V
 Landroid/nfc/INfcAdapterExtras;->close(Ljava/lang/String;Landroid/os/IBinder;)Landroid/os/Bundle;
@@ -3510,21 +3353,6 @@
 Landroid/nfc/INfcAdapterExtras;->open(Ljava/lang/String;Landroid/os/IBinder;)Landroid/os/Bundle;
 Landroid/nfc/INfcAdapterExtras;->setCardEmulationRoute(Ljava/lang/String;I)V
 Landroid/nfc/INfcAdapterExtras;->transceive(Ljava/lang/String;[B)Landroid/os/Bundle;
-Landroid/nfc/NdefRecord;->mId:[B
-Landroid/nfc/NfcActivityManager;->mAdapter:Landroid/nfc/NfcAdapter;
-Landroid/nfc/NfcAdapter;->attemptDeadServiceRecovery(Ljava/lang/Exception;)V
-Landroid/nfc/NfcAdapter;->getAdapterState()I
-Landroid/nfc/NfcAdapter;->getContext()Landroid/content/Context;
-Landroid/nfc/NfcAdapter;->getDefaultAdapter()Landroid/nfc/NfcAdapter;
-Landroid/nfc/NfcAdapter;->getNfcAdapter(Landroid/content/Context;)Landroid/nfc/NfcAdapter;
-Landroid/nfc/NfcAdapter;->getNfcAdapterExtrasInterface()Landroid/nfc/INfcAdapterExtras;
-Landroid/nfc/NfcAdapter;->getService()Landroid/nfc/INfcAdapter;
-Landroid/nfc/NfcAdapter;->setNdefPushMessageCallback(Landroid/nfc/NfcAdapter$CreateNdefMessageCallback;Landroid/app/Activity;I)V
-Landroid/nfc/NfcAdapter;->sService:Landroid/nfc/INfcAdapter;
-Landroid/nfc/NfcManager;-><init>(Landroid/content/Context;)V
-Landroid/nfc/Tag;->getServiceHandle()I
-Landroid/nfc/Tag;->getTagService()Landroid/nfc/INfcTag;
-Landroid/nfc/Tag;->mId:[B
 Landroid/opengl/EGL14;->eglGetDisplay(J)Landroid/opengl/EGLDisplay;
 Landroid/opengl/GLES20;->glGetActiveAttrib(IIILjava/nio/IntBuffer;Ljava/nio/IntBuffer;Ljava/nio/IntBuffer;B)V
 Landroid/opengl/GLES20;->glGetActiveUniform(IIILjava/nio/IntBuffer;Ljava/nio/IntBuffer;Ljava/nio/IntBuffer;B)V
@@ -4216,9 +4044,6 @@
 Landroid/provider/CalendarContract$CalendarAlerts;->rescheduleMissedAlarms(Landroid/content/ContentResolver;Landroid/content/Context;Landroid/app/AlarmManager;)V
 Landroid/provider/CalendarContract$CalendarAlerts;->scheduleAlarm(Landroid/content/Context;Landroid/app/AlarmManager;J)V
 Landroid/provider/CallLog$Calls;->addCall(Lcom/android/internal/telephony/CallerInfo;Landroid/content/Context;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;IIILandroid/telecom/PhoneAccountHandle;JILjava/lang/Long;ZLandroid/os/UserHandle;Z)Landroid/net/Uri;
-Landroid/provider/ContactsContract$ContactCounts;->EXTRA_ADDRESS_BOOK_INDEX:Ljava/lang/String;
-Landroid/provider/ContactsContract$ContactCounts;->EXTRA_ADDRESS_BOOK_INDEX_COUNTS:Ljava/lang/String;
-Landroid/provider/ContactsContract$ContactCounts;->EXTRA_ADDRESS_BOOK_INDEX_TITLES:Ljava/lang/String;
 Landroid/provider/ContactsContract$Contacts$AggregationSuggestions;->builder()Landroid/provider/ContactsContract$Contacts$AggregationSuggestions$Builder;
 Landroid/provider/ContactsContract$Contacts;->CORP_CONTENT_URI:Landroid/net/Uri;
 Landroid/provider/ContactsContract$QuickContact;->composeQuickContactsIntent(Landroid/content/Context;Landroid/graphics/Rect;Landroid/net/Uri;I[Ljava/lang/String;)Landroid/content/Intent;
@@ -4742,7 +4567,7 @@
 Landroid/renderscript/RSSurfaceView;-><init>(Landroid/content/Context;Landroid/util/AttributeSet;)V
 Landroid/renderscript/Script$Builder;-><init>(Landroid/renderscript/RenderScript;)V
 Landroid/renderscript/Script$Builder;->mRS:Landroid/renderscript/RenderScript;
-Landroid/security/Credentials;->convertToPem([[Ljava/security/cert/Certificate;)[B
+Landroid/security/Credentials;->convertToPem([Ljava/security/cert/Certificate;)[B
 Landroid/security/Credentials;->getInstance()Landroid/security/Credentials;
 Landroid/security/Credentials;->install(Landroid/content/Context;Ljava/lang/String;[B)V
 Landroid/security/Credentials;->install(Landroid/content/Context;Ljava/security/KeyPair;)V
@@ -5609,21 +5434,6 @@
 Landroid/view/AccessibilityIterators$AbstractTextSegmentIterator;->mText:Ljava/lang/String;
 Landroid/view/ActionProvider;->reset()V
 Landroid/view/ActionProvider;->setSubUiVisibilityListener(Landroid/view/ActionProvider$SubUiVisibilityListener;)V
-Landroid/view/animation/Animation;->detach()V
-Landroid/view/animation/Animation;->getInvalidateRegion(IIIILandroid/graphics/RectF;Landroid/view/animation/Transformation;)V
-Landroid/view/animation/Animation;->initializeInvalidateRegion(IIII)V
-Landroid/view/animation/Animation;->mListener:Landroid/view/animation/Animation$AnimationListener;
-Landroid/view/animation/Animation;->mPreviousRegion:Landroid/graphics/RectF;
-Landroid/view/animation/Animation;->mPreviousTransformation:Landroid/view/animation/Transformation;
-Landroid/view/animation/Animation;->mRegion:Landroid/graphics/RectF;
-Landroid/view/animation/Animation;->mTransformation:Landroid/view/animation/Transformation;
-Landroid/view/animation/AnimationUtils;->createAnimationFromXml(Landroid/content/Context;Lorg/xmlpull/v1/XmlPullParser;Landroid/view/animation/AnimationSet;Landroid/util/AttributeSet;)Landroid/view/animation/Animation;
-Landroid/view/animation/Transformation;->printShortString(Ljava/io/PrintWriter;)V
-Landroid/view/animation/TranslateAnimation;->mFromXValue:F
-Landroid/view/animation/TranslateAnimation;->mFromYValue:F
-Landroid/view/animation/TranslateAnimation;->mToXValue:F
-Landroid/view/animation/TranslateAnimation;->mToYValue:F
-Landroid/view/animation/TranslateYAnimation;-><init>(IFIF)V
 Landroid/view/autofill/IAutoFillManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
 Landroid/view/autofill/IAutoFillManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/view/autofill/IAutoFillManager;
 Landroid/view/Choreographer$CallbackQueue;->addCallbackLocked(JLjava/lang/Object;Ljava/lang/Object;)V
@@ -6239,7 +6049,6 @@
 Landroid/view/ViewHierarchyEncoder;->addProperty(Ljava/lang/String;Z)V
 Landroid/view/ViewOverlay;->getOverlayView()Landroid/view/ViewGroup;
 Landroid/view/ViewOverlay;->isEmpty()Z
-Landroid/view/ViewPropertyAnimator;->mRTBackend:Landroid/view/ViewPropertyAnimatorRT;
 Landroid/view/ViewRootImpl$CalledFromWrongThreadException;-><init>(Ljava/lang/String;)V
 Landroid/view/ViewRootImpl;->addConfigCallback(Landroid/view/ViewRootImpl$ConfigChangedCallback;)V
 Landroid/view/ViewRootImpl;->cancelInvalidate(Landroid/view/View;)V
@@ -6420,7 +6229,6 @@
 Landroid/webkit/WebResourceResponse;->mImmutable:Z
 Landroid/webkit/WebResourceResponse;->mStatusCode:I
 Landroid/webkit/WebSettings$TextSize;->value:I
-Landroid/webkit/WebSyncManager;->mHandler:Landroid/os/Handler;
 Landroid/webkit/WebSyncManager;->syncFromRamToFlash()V
 Landroid/webkit/WebView;-><init>(Landroid/content/Context;Landroid/util/AttributeSet;IILjava/util/Map;Z)V
 Landroid/webkit/WebView;-><init>(Landroid/content/Context;Landroid/util/AttributeSet;ILjava/util/Map;Z)V
@@ -7093,73 +6901,6 @@
 Lcom/android/ims/internal/IImsVideoCallCallback;->receiveSessionModifyResponse(ILandroid/telecom/VideoProfile;Landroid/telecom/VideoProfile;)V
 Lcom/android/ims/internal/IImsVideoCallProvider$Stub;-><init>()V
 Lcom/android/ims/internal/IImsVideoCallProvider;->setCallback(Lcom/android/ims/internal/IImsVideoCallCallback;)V
-Lcom/android/ims/internal/uce/common/CapInfo;-><init>()V
-Lcom/android/ims/internal/uce/common/CapInfo;->setCapTimestamp(J)V
-Lcom/android/ims/internal/uce/common/CapInfo;->setCdViaPresenceSupported(Z)V
-Lcom/android/ims/internal/uce/common/CapInfo;->setExts([Ljava/lang/String;)V
-Lcom/android/ims/internal/uce/common/CapInfo;->setFtHttpSupported(Z)V
-Lcom/android/ims/internal/uce/common/CapInfo;->setFtSnFSupported(Z)V
-Lcom/android/ims/internal/uce/common/CapInfo;->setFtSupported(Z)V
-Lcom/android/ims/internal/uce/common/CapInfo;->setFtThumbSupported(Z)V
-Lcom/android/ims/internal/uce/common/CapInfo;->setFullSnFGroupChatSupported(Z)V
-Lcom/android/ims/internal/uce/common/CapInfo;->setGeoPullFtSupported(Z)V
-Lcom/android/ims/internal/uce/common/CapInfo;->setGeoPullSupported(Z)V
-Lcom/android/ims/internal/uce/common/CapInfo;->setGeoPushSupported(Z)V
-Lcom/android/ims/internal/uce/common/CapInfo;->setImSupported(Z)V
-Lcom/android/ims/internal/uce/common/CapInfo;->setIpVideoSupported(Z)V
-Lcom/android/ims/internal/uce/common/CapInfo;->setIpVoiceSupported(Z)V
-Lcom/android/ims/internal/uce/common/CapInfo;->setIsSupported(Z)V
-Lcom/android/ims/internal/uce/common/CapInfo;->setRcsIpVideoCallSupported(Z)V
-Lcom/android/ims/internal/uce/common/CapInfo;->setRcsIpVideoOnlyCallSupported(Z)V
-Lcom/android/ims/internal/uce/common/CapInfo;->setRcsIpVoiceCallSupported(Z)V
-Lcom/android/ims/internal/uce/common/CapInfo;->setSmSupported(Z)V
-Lcom/android/ims/internal/uce/common/CapInfo;->setSpSupported(Z)V
-Lcom/android/ims/internal/uce/common/CapInfo;->setVsDuringCSSupported(Z)V
-Lcom/android/ims/internal/uce/common/CapInfo;->setVsSupported(Z)V
-Lcom/android/ims/internal/uce/common/StatusCode;-><init>()V
-Lcom/android/ims/internal/uce/common/StatusCode;->setStatusCode(I)V
-Lcom/android/ims/internal/uce/common/UceLong;->getUceLong()J
-Lcom/android/ims/internal/uce/common/UceLong;->setUceLong(J)V
-Lcom/android/ims/internal/uce/presence/PresCmdId;-><init>()V
-Lcom/android/ims/internal/uce/presence/PresCmdId;->setCmdId(I)V
-Lcom/android/ims/internal/uce/presence/PresCmdStatus;-><init>()V
-Lcom/android/ims/internal/uce/presence/PresCmdStatus;->setCmdId(Lcom/android/ims/internal/uce/presence/PresCmdId;)V
-Lcom/android/ims/internal/uce/presence/PresCmdStatus;->setRequestId(I)V
-Lcom/android/ims/internal/uce/presence/PresCmdStatus;->setStatus(Lcom/android/ims/internal/uce/common/StatusCode;)V
-Lcom/android/ims/internal/uce/presence/PresCmdStatus;->setUserData(I)V
-Lcom/android/ims/internal/uce/presence/PresPublishTriggerType;-><init>()V
-Lcom/android/ims/internal/uce/presence/PresPublishTriggerType;->setPublishTrigeerType(I)V
-Lcom/android/ims/internal/uce/presence/PresResInfo;-><init>()V
-Lcom/android/ims/internal/uce/presence/PresResInfo;->setDisplayName(Ljava/lang/String;)V
-Lcom/android/ims/internal/uce/presence/PresResInfo;->setInstanceInfo(Lcom/android/ims/internal/uce/presence/PresResInstanceInfo;)V
-Lcom/android/ims/internal/uce/presence/PresResInfo;->setResUri(Ljava/lang/String;)V
-Lcom/android/ims/internal/uce/presence/PresResInstanceInfo;-><init>()V
-Lcom/android/ims/internal/uce/presence/PresResInstanceInfo;->setPresentityUri(Ljava/lang/String;)V
-Lcom/android/ims/internal/uce/presence/PresResInstanceInfo;->setReason(Ljava/lang/String;)V
-Lcom/android/ims/internal/uce/presence/PresResInstanceInfo;->setResId(Ljava/lang/String;)V
-Lcom/android/ims/internal/uce/presence/PresResInstanceInfo;->setResInstanceState(I)V
-Lcom/android/ims/internal/uce/presence/PresResInstanceInfo;->setTupleInfo([Lcom/android/ims/internal/uce/presence/PresTupleInfo;)V
-Lcom/android/ims/internal/uce/presence/PresRlmiInfo;-><init>()V
-Lcom/android/ims/internal/uce/presence/PresRlmiInfo;->setFullState(Z)V
-Lcom/android/ims/internal/uce/presence/PresRlmiInfo;->setListName(Ljava/lang/String;)V
-Lcom/android/ims/internal/uce/presence/PresRlmiInfo;->setPresSubscriptionState(Lcom/android/ims/internal/uce/presence/PresSubscriptionState;)V
-Lcom/android/ims/internal/uce/presence/PresRlmiInfo;->setRequestId(I)V
-Lcom/android/ims/internal/uce/presence/PresRlmiInfo;->setSubscriptionExpireTime(I)V
-Lcom/android/ims/internal/uce/presence/PresRlmiInfo;->setSubscriptionTerminatedReason(Ljava/lang/String;)V
-Lcom/android/ims/internal/uce/presence/PresRlmiInfo;->setUri(Ljava/lang/String;)V
-Lcom/android/ims/internal/uce/presence/PresRlmiInfo;->setVersion(I)V
-Lcom/android/ims/internal/uce/presence/PresSipResponse;-><init>()V
-Lcom/android/ims/internal/uce/presence/PresSipResponse;->setCmdId(Lcom/android/ims/internal/uce/presence/PresCmdId;)V
-Lcom/android/ims/internal/uce/presence/PresSipResponse;->setReasonPhrase(Ljava/lang/String;)V
-Lcom/android/ims/internal/uce/presence/PresSipResponse;->setRequestId(I)V
-Lcom/android/ims/internal/uce/presence/PresSipResponse;->setRetryAfter(I)V
-Lcom/android/ims/internal/uce/presence/PresSipResponse;->setSipResponseCode(I)V
-Lcom/android/ims/internal/uce/presence/PresSubscriptionState;-><init>()V
-Lcom/android/ims/internal/uce/presence/PresSubscriptionState;->setPresSubscriptionState(I)V
-Lcom/android/ims/internal/uce/presence/PresTupleInfo;-><init>()V
-Lcom/android/ims/internal/uce/presence/PresTupleInfo;->setContactUri(Ljava/lang/String;)V
-Lcom/android/ims/internal/uce/presence/PresTupleInfo;->setFeatureTag(Ljava/lang/String;)V
-Lcom/android/ims/internal/uce/presence/PresTupleInfo;->setTimestamp(Ljava/lang/String;)V
 Lcom/android/ims/internal/uce/uceservice/IUceListener$Stub;-><init>()V
 Lcom/android/internal/app/AlertController$AlertParams;-><init>(Landroid/content/Context;)V
 Lcom/android/internal/app/AlertController$AlertParams;->apply(Lcom/android/internal/app/AlertController;)V
@@ -8122,6 +7863,7 @@
 Lcom/android/internal/util/AsyncChannel;->sendMessageSynchronously(Landroid/os/Message;)Landroid/os/Message;
 Lcom/android/internal/util/AsyncChannel;->STATUS_SUCCESSFUL:I
 Lcom/android/internal/util/FastPrintWriter;-><init>(Ljava/io/OutputStream;)V
+Lcom/android/internal/util/HexDump;->toHexString([BZ)Ljava/lang/String;
 Lcom/android/internal/util/XmlUtils;->convertValueToBoolean(Ljava/lang/CharSequence;Z)Z
 Lcom/android/internal/util/XmlUtils;->convertValueToInt(Ljava/lang/CharSequence;I)I
 Lcom/android/internal/util/XmlUtils;->readMapXml(Ljava/io/InputStream;)Ljava/util/HashMap;
@@ -8211,6 +7953,7 @@
 Lcom/android/internal/widget/IRemoteViewsFactory;->hasStableIds()Z
 Lcom/android/internal/widget/IRemoteViewsFactory;->isCreated()Z
 Lcom/android/internal/widget/IRemoteViewsFactory;->onDataSetChanged()V
+Lcom/android/internal/widget/ScrollBarUtils;->getThumbLength(IIII)I
 Lcom/android/internal/widget/ScrollingTabContainerView;-><init>(Landroid/content/Context;)V
 Lcom/android/internal/widget/ScrollingTabContainerView;->addTab(Landroid/app/ActionBar$Tab;IZ)V
 Lcom/android/internal/widget/ScrollingTabContainerView;->addTab(Landroid/app/ActionBar$Tab;Z)V
@@ -8282,6 +8025,8 @@
 Lcom/android/org/conscrypt/AbstractConscryptSocket;->setNpnProtocols([B)V
 Lcom/android/org/conscrypt/AbstractConscryptSocket;->setSoWriteTimeout(I)V
 Lcom/android/org/conscrypt/AbstractConscryptSocket;->setUseSessionTickets(Z)V
+Lcom/android/org/conscrypt/ConscryptFileDescriptorSocket;->setHostname(Ljava/lang/String;)V
+Lcom/android/org/conscrypt/ConscryptFileDescriptorSocket;->setUseSessionTickets(Z)V
 Lcom/android/org/conscrypt/ConscryptSocketBase;->getHostname()Ljava/lang/String;
 Lcom/android/org/conscrypt/ConscryptSocketBase;->getHostnameOrIP()Ljava/lang/String;
 Lcom/android/org/conscrypt/ConscryptSocketBase;->getSoWriteTimeout()I
@@ -8751,46 +8496,6 @@
 Ljava/util/zip/Inflater;->len:I
 Ljava/util/zip/Inflater;->needDict:Z
 Ljava/util/zip/Inflater;->off:I
-Ljava/util/zip/ZipConstants;->CENATT:I
-Ljava/util/zip/ZipConstants;->CENATX:I
-Ljava/util/zip/ZipConstants;->CENCOM:I
-Ljava/util/zip/ZipConstants;->CENCRC:I
-Ljava/util/zip/ZipConstants;->CENDSK:I
-Ljava/util/zip/ZipConstants;->CENEXT:I
-Ljava/util/zip/ZipConstants;->CENFLG:I
-Ljava/util/zip/ZipConstants;->CENHDR:I
-Ljava/util/zip/ZipConstants;->CENHOW:I
-Ljava/util/zip/ZipConstants;->CENLEN:I
-Ljava/util/zip/ZipConstants;->CENNAM:I
-Ljava/util/zip/ZipConstants;->CENOFF:I
-Ljava/util/zip/ZipConstants;->CENSIG:J
-Ljava/util/zip/ZipConstants;->CENSIZ:I
-Ljava/util/zip/ZipConstants;->CENTIM:I
-Ljava/util/zip/ZipConstants;->CENVEM:I
-Ljava/util/zip/ZipConstants;->CENVER:I
-Ljava/util/zip/ZipConstants;->ENDCOM:I
-Ljava/util/zip/ZipConstants;->ENDHDR:I
-Ljava/util/zip/ZipConstants;->ENDOFF:I
-Ljava/util/zip/ZipConstants;->ENDSIG:J
-Ljava/util/zip/ZipConstants;->ENDSIZ:I
-Ljava/util/zip/ZipConstants;->ENDSUB:I
-Ljava/util/zip/ZipConstants;->ENDTOT:I
-Ljava/util/zip/ZipConstants;->EXTCRC:I
-Ljava/util/zip/ZipConstants;->EXTHDR:I
-Ljava/util/zip/ZipConstants;->EXTLEN:I
-Ljava/util/zip/ZipConstants;->EXTSIG:J
-Ljava/util/zip/ZipConstants;->EXTSIZ:I
-Ljava/util/zip/ZipConstants;->LOCCRC:I
-Ljava/util/zip/ZipConstants;->LOCEXT:I
-Ljava/util/zip/ZipConstants;->LOCFLG:I
-Ljava/util/zip/ZipConstants;->LOCHDR:I
-Ljava/util/zip/ZipConstants;->LOCHOW:I
-Ljava/util/zip/ZipConstants;->LOCLEN:I
-Ljava/util/zip/ZipConstants;->LOCNAM:I
-Ljava/util/zip/ZipConstants;->LOCSIG:J
-Ljava/util/zip/ZipConstants;->LOCSIZ:I
-Ljava/util/zip/ZipConstants;->LOCTIM:I
-Ljava/util/zip/ZipConstants;->LOCVER:I
 Ljava/util/zip/ZipEntry;-><init>(Ljava/lang/String;Ljava/lang/String;JJJII[BJ)V
 Ljava/util/zip/ZipEntry;->method:I
 Ljava/util/zip/ZipFile;->close(J)V
@@ -8805,6 +8510,16 @@
 Ljavax/net/ssl/SSLServerSocketFactory;->defaultServerSocketFactory:Ljavax/net/ssl/SSLServerSocketFactory;
 Ljavax/net/ssl/SSLSocketFactory;->createSocket(Ljava/net/Socket;Ljava/io/InputStream;Z)Ljava/net/Socket;
 Ljavax/net/ssl/SSLSocketFactory;->defaultSocketFactory:Ljavax/net/ssl/SSLSocketFactory;
+Llibcore/icu/ICU;->addLikelySubtags(Ljava/util/Locale;)Ljava/util/Locale;
+Llibcore/io/Memory;->peekByte(J)B
+Llibcore/io/Memory;->peekByteArray(J[BII)V
+Llibcore/io/Memory;->peekInt(JZ)I
+Llibcore/io/Memory;->peekLong(JZ)J
+Llibcore/io/Memory;->pokeByte(JB)V
+Llibcore/io/Memory;->pokeByteArray(J[BII)V
+Llibcore/io/Memory;->pokeInt(JIZ)V
+Llibcore/io/Memory;->pokeLong(JJZ)V
+Llibcore/io/Streams;->copy(Ljava/io/InputStream;Ljava/io/OutputStream;)I
 Llibcore/util/BasicLruCache;->map:Ljava/util/LinkedHashMap;
 Llibcore/util/ZoneInfo;->mTransitions:[J
 Lorg/apache/http/conn/ssl/AbstractVerifier;->BAD_COUNTRY_2LDS:[Ljava/lang/String;
@@ -8827,6 +8542,8 @@
 Lorg/ccil/cowan/tagsoup/ElementType;->theNamespace:Ljava/lang/String;
 Lorg/ccil/cowan/tagsoup/ElementType;->theParent:Lorg/ccil/cowan/tagsoup/ElementType;
 Lorg/ccil/cowan/tagsoup/ElementType;->theSchema:Lorg/ccil/cowan/tagsoup/Schema;
+Lorg/ccil/cowan/tagsoup/HTMLSchema;-><init>()V
+Lorg/ccil/cowan/tagsoup/Parser;-><init>()V
 Lorg/ccil/cowan/tagsoup/Schema;->theElementTypes:Ljava/util/HashMap;
 Lorg/ccil/cowan/tagsoup/Schema;->theEntities:Ljava/util/HashMap;
 Lorg/ccil/cowan/tagsoup/Schema;->thePrefix:Ljava/lang/String;
@@ -8926,10 +8643,84 @@
 Lorg/xml/sax/SAXParseException;->lineNumber:I
 Lorg/xml/sax/SAXParseException;->publicId:Ljava/lang/String;
 Lorg/xml/sax/SAXParseException;->systemId:Ljava/lang/String;
+Lsun/misc/Cleaner;->clean()V
+Lsun/misc/Unsafe;->addressSize()I
+Lsun/misc/Unsafe;->allocateInstance(Ljava/lang/Class;)Ljava/lang/Object;
+Lsun/misc/Unsafe;->allocateMemory(J)J
+Lsun/misc/Unsafe;->arrayBaseOffset(Ljava/lang/Class;)I
+Lsun/misc/Unsafe;->arrayIndexScale(Ljava/lang/Class;)I
+Lsun/misc/Unsafe;->compareAndSwapInt(Ljava/lang/Object;JII)Z
+Lsun/misc/Unsafe;->compareAndSwapLong(Ljava/lang/Object;JJJ)Z
+Lsun/misc/Unsafe;->compareAndSwapObject(Ljava/lang/Object;JLjava/lang/Object;Ljava/lang/Object;)Z
+Lsun/misc/Unsafe;->copyMemory(JJJ)V
+Lsun/misc/Unsafe;->copyMemoryFromPrimitiveArray(Ljava/lang/Object;JJJ)V
+Lsun/misc/Unsafe;->copyMemoryToPrimitiveArray(JLjava/lang/Object;JJ)V
+Lsun/misc/Unsafe;->freeMemory(J)V
+Lsun/misc/Unsafe;->fullFence()V
+Lsun/misc/Unsafe;->getAndAddInt(Ljava/lang/Object;JI)I
+Lsun/misc/Unsafe;->getAndAddLong(Ljava/lang/Object;JJ)J
+Lsun/misc/Unsafe;->getAndSetInt(Ljava/lang/Object;JI)I
+Lsun/misc/Unsafe;->getAndSetLong(Ljava/lang/Object;JJ)J
+Lsun/misc/Unsafe;->getAndSetObject(Ljava/lang/Object;JLjava/lang/Object;)Ljava/lang/Object;
+Lsun/misc/Unsafe;->getArrayBaseOffsetForComponentType(Ljava/lang/Class;)I
+Lsun/misc/Unsafe;->getArrayIndexScaleForComponentType(Ljava/lang/Class;)I
+Lsun/misc/Unsafe;->getBoolean(Ljava/lang/Object;J)Z
+Lsun/misc/Unsafe;->getByte(J)B
+Lsun/misc/Unsafe;->getByte(Ljava/lang/Object;J)B
+Lsun/misc/Unsafe;->getChar(J)C
+Lsun/misc/Unsafe;->getChar(Ljava/lang/Object;J)C
+Lsun/misc/Unsafe;->getDouble(J)D
+Lsun/misc/Unsafe;->getDouble(Ljava/lang/Object;J)D
+Lsun/misc/Unsafe;->getFloat(J)F
+Lsun/misc/Unsafe;->getFloat(Ljava/lang/Object;J)F
+Lsun/misc/Unsafe;->getInt(J)I
+Lsun/misc/Unsafe;->getInt(Ljava/lang/Object;J)I
+Lsun/misc/Unsafe;->getIntVolatile(Ljava/lang/Object;J)I
+Lsun/misc/Unsafe;->getLong(J)J
+Lsun/misc/Unsafe;->getLong(Ljava/lang/Object;J)J
+Lsun/misc/Unsafe;->getLongVolatile(Ljava/lang/Object;J)J
+Lsun/misc/Unsafe;->getObject(Ljava/lang/Object;J)Ljava/lang/Object;
+Lsun/misc/Unsafe;->getObjectVolatile(Ljava/lang/Object;J)Ljava/lang/Object;
+Lsun/misc/Unsafe;->getShort(J)S
+Lsun/misc/Unsafe;->getShort(Ljava/lang/Object;J)S
+Lsun/misc/Unsafe;->getUnsafe()Lsun/misc/Unsafe;
+Lsun/misc/Unsafe;->INVALID_FIELD_OFFSET:I
+Lsun/misc/Unsafe;->loadFence()V
+Lsun/misc/Unsafe;->objectFieldOffset(Ljava/lang/reflect/Field;)J
+Lsun/misc/Unsafe;->pageSize()I
+Lsun/misc/Unsafe;->park(ZJ)V
+Lsun/misc/Unsafe;->putBoolean(Ljava/lang/Object;JZ)V
+Lsun/misc/Unsafe;->putByte(JB)V
+Lsun/misc/Unsafe;->putByte(Ljava/lang/Object;JB)V
+Lsun/misc/Unsafe;->putChar(JC)V
+Lsun/misc/Unsafe;->putChar(Ljava/lang/Object;JC)V
+Lsun/misc/Unsafe;->putDouble(JD)V
+Lsun/misc/Unsafe;->putDouble(Ljava/lang/Object;JD)V
+Lsun/misc/Unsafe;->putFloat(JF)V
+Lsun/misc/Unsafe;->putFloat(Ljava/lang/Object;JF)V
+Lsun/misc/Unsafe;->putInt(JI)V
+Lsun/misc/Unsafe;->putInt(Ljava/lang/Object;JI)V
+Lsun/misc/Unsafe;->putIntVolatile(Ljava/lang/Object;JI)V
+Lsun/misc/Unsafe;->putLong(JJ)V
+Lsun/misc/Unsafe;->putLong(Ljava/lang/Object;JJ)V
+Lsun/misc/Unsafe;->putLongVolatile(Ljava/lang/Object;JJ)V
+Lsun/misc/Unsafe;->putObject(Ljava/lang/Object;JLjava/lang/Object;)V
+Lsun/misc/Unsafe;->putObjectVolatile(Ljava/lang/Object;JLjava/lang/Object;)V
+Lsun/misc/Unsafe;->putOrderedInt(Ljava/lang/Object;JI)V
+Lsun/misc/Unsafe;->putOrderedLong(Ljava/lang/Object;JJ)V
+Lsun/misc/Unsafe;->putOrderedObject(Ljava/lang/Object;JLjava/lang/Object;)V
+Lsun/misc/Unsafe;->putShort(JS)V
+Lsun/misc/Unsafe;->putShort(Ljava/lang/Object;JS)V
+Lsun/misc/Unsafe;->setMemory(JJB)V
+Lsun/misc/Unsafe;->storeFence()V
 Lsun/misc/Unsafe;->theUnsafe:Lsun/misc/Unsafe;
 Lsun/misc/Unsafe;->THE_ONE:Lsun/misc/Unsafe;
+Lsun/misc/Unsafe;->unpark(Ljava/lang/Object;)V
 Lsun/misc/URLClassPath$JarLoader;->getJarFile()Ljava/util/jar/JarFile;
 Lsun/misc/URLClassPath;->lmap:Ljava/util/HashMap;
 Lsun/misc/URLClassPath;->loaders:Ljava/util/ArrayList;
 Lsun/misc/URLClassPath;->urls:Ljava/util/Stack;
+Lsun/nio/ch/DirectBuffer;->cleaner()Lsun/misc/Cleaner;
+Lsun/security/x509/AlgorithmId;->get(Ljava/lang/String;)Lsun/security/x509/AlgorithmId;
+Lsun/security/x509/AlgorithmId;->getName()Ljava/lang/String;
 Lsun/security/x509/AVA;->hasRFC2253Keyword()Z
diff --git a/config/hiddenapi-vendor-list.txt b/config/hiddenapi-vendor-list.txt
index 85621c6..b8c1468 100644
--- a/config/hiddenapi-vendor-list.txt
+++ b/config/hiddenapi-vendor-list.txt
@@ -605,31 +605,6 @@
 Lcom/android/ims/internal/IImsUtListener;->utConfigurationQueryFailed(Lcom/android/ims/internal/IImsUt;ILandroid/telephony/ims/ImsReasonInfo;)V
 Lcom/android/ims/internal/IImsUtListener;->utConfigurationUpdated(Lcom/android/ims/internal/IImsUt;I)V
 Lcom/android/ims/internal/IImsUtListener;->utConfigurationUpdateFailed(Lcom/android/ims/internal/IImsUt;ILandroid/telephony/ims/ImsReasonInfo;)V
-Lcom/android/ims/internal/uce/common/CapInfo;->getCapTimestamp()J
-Lcom/android/ims/internal/uce/common/CapInfo;->isCdViaPresenceSupported()Z
-Lcom/android/ims/internal/uce/common/CapInfo;->isFtHttpSupported()Z
-Lcom/android/ims/internal/uce/common/CapInfo;->isFtSnFSupported()Z
-Lcom/android/ims/internal/uce/common/CapInfo;->isFtSupported()Z
-Lcom/android/ims/internal/uce/common/CapInfo;->isFtThumbSupported()Z
-Lcom/android/ims/internal/uce/common/CapInfo;->isFullSnFGroupChatSupported()Z
-Lcom/android/ims/internal/uce/common/CapInfo;->isGeoPullFtSupported()Z
-Lcom/android/ims/internal/uce/common/CapInfo;->isGeoPullSupported()Z
-Lcom/android/ims/internal/uce/common/CapInfo;->isGeoPushSupported()Z
-Lcom/android/ims/internal/uce/common/CapInfo;->isImSupported()Z
-Lcom/android/ims/internal/uce/common/CapInfo;->isIpVideoSupported()Z
-Lcom/android/ims/internal/uce/common/CapInfo;->isIpVoiceSupported()Z
-Lcom/android/ims/internal/uce/common/CapInfo;->isIsSupported()Z
-Lcom/android/ims/internal/uce/common/CapInfo;->isRcsIpVideoCallSupported()Z
-Lcom/android/ims/internal/uce/common/CapInfo;->isRcsIpVideoOnlyCallSupported()Z
-Lcom/android/ims/internal/uce/common/CapInfo;->isRcsIpVoiceCallSupported()Z
-Lcom/android/ims/internal/uce/common/CapInfo;->isSmSupported()Z
-Lcom/android/ims/internal/uce/common/CapInfo;->isSpSupported()Z
-Lcom/android/ims/internal/uce/common/CapInfo;->isVsDuringCSSupported()Z
-Lcom/android/ims/internal/uce/common/CapInfo;->isVsSupported()Z
-Lcom/android/ims/internal/uce/common/StatusCode;->getStatusCode()I
-Lcom/android/ims/internal/uce/common/UceLong;-><init>()V
-Lcom/android/ims/internal/uce/common/UceLong;->getClientId()I
-Lcom/android/ims/internal/uce/common/UceLong;->setClientId(I)V
 Lcom/android/ims/internal/uce/options/IOptionsListener;->cmdStatus(Lcom/android/ims/internal/uce/options/OptionsCmdStatus;)V
 Lcom/android/ims/internal/uce/options/IOptionsListener;->getVersionCb(Ljava/lang/String;)V
 Lcom/android/ims/internal/uce/options/IOptionsListener;->incomingOptions(Ljava/lang/String;Lcom/android/ims/internal/uce/options/OptionsCapInfo;I)V
@@ -645,24 +620,6 @@
 Lcom/android/ims/internal/uce/options/IOptionsService;->removeListener(ILcom/android/ims/internal/uce/common/UceLong;)Lcom/android/ims/internal/uce/common/StatusCode;
 Lcom/android/ims/internal/uce/options/IOptionsService;->responseIncomingOptions(IIILjava/lang/String;Lcom/android/ims/internal/uce/options/OptionsCapInfo;Z)Lcom/android/ims/internal/uce/common/StatusCode;
 Lcom/android/ims/internal/uce/options/IOptionsService;->setMyInfo(ILcom/android/ims/internal/uce/common/CapInfo;I)Lcom/android/ims/internal/uce/common/StatusCode;
-Lcom/android/ims/internal/uce/options/OptionsCapInfo;-><init>()V
-Lcom/android/ims/internal/uce/options/OptionsCapInfo;->getCapInfo()Lcom/android/ims/internal/uce/common/CapInfo;
-Lcom/android/ims/internal/uce/options/OptionsCapInfo;->getSdp()Ljava/lang/String;
-Lcom/android/ims/internal/uce/options/OptionsCapInfo;->setCapInfo(Lcom/android/ims/internal/uce/common/CapInfo;)V
-Lcom/android/ims/internal/uce/options/OptionsCapInfo;->setSdp(Ljava/lang/String;)V
-Lcom/android/ims/internal/uce/options/OptionsCmdId;-><init>()V
-Lcom/android/ims/internal/uce/options/OptionsCmdId;->setCmdId(I)V
-Lcom/android/ims/internal/uce/options/OptionsCmdStatus;-><init>()V
-Lcom/android/ims/internal/uce/options/OptionsCmdStatus;->setCapInfo(Lcom/android/ims/internal/uce/common/CapInfo;)V
-Lcom/android/ims/internal/uce/options/OptionsCmdStatus;->setCmdId(Lcom/android/ims/internal/uce/options/OptionsCmdId;)V
-Lcom/android/ims/internal/uce/options/OptionsCmdStatus;->setStatus(Lcom/android/ims/internal/uce/common/StatusCode;)V
-Lcom/android/ims/internal/uce/options/OptionsCmdStatus;->setUserData(I)V
-Lcom/android/ims/internal/uce/options/OptionsSipResponse;-><init>()V
-Lcom/android/ims/internal/uce/options/OptionsSipResponse;->setCmdId(Lcom/android/ims/internal/uce/options/OptionsCmdId;)V
-Lcom/android/ims/internal/uce/options/OptionsSipResponse;->setReasonPhrase(Ljava/lang/String;)V
-Lcom/android/ims/internal/uce/options/OptionsSipResponse;->setRequestId(I)V
-Lcom/android/ims/internal/uce/options/OptionsSipResponse;->setRetryAfter(I)V
-Lcom/android/ims/internal/uce/options/OptionsSipResponse;->setSipResponseCode(I)V
 Lcom/android/ims/internal/uce/presence/IPresenceListener;->capInfoReceived(Ljava/lang/String;[Lcom/android/ims/internal/uce/presence/PresTupleInfo;)V
 Lcom/android/ims/internal/uce/presence/IPresenceListener;->cmdStatus(Lcom/android/ims/internal/uce/presence/PresCmdStatus;)V
 Lcom/android/ims/internal/uce/presence/IPresenceListener;->getVersionCb(Ljava/lang/String;)V
@@ -681,18 +638,6 @@
 Lcom/android/ims/internal/uce/presence/IPresenceService;->reenableService(II)Lcom/android/ims/internal/uce/common/StatusCode;
 Lcom/android/ims/internal/uce/presence/IPresenceService;->removeListener(ILcom/android/ims/internal/uce/common/UceLong;)Lcom/android/ims/internal/uce/common/StatusCode;
 Lcom/android/ims/internal/uce/presence/IPresenceService;->setNewFeatureTag(ILjava/lang/String;Lcom/android/ims/internal/uce/presence/PresServiceInfo;I)Lcom/android/ims/internal/uce/common/StatusCode;
-Lcom/android/ims/internal/uce/presence/PresCapInfo;->getCapInfo()Lcom/android/ims/internal/uce/common/CapInfo;
-Lcom/android/ims/internal/uce/presence/PresCapInfo;->getContactUri()Ljava/lang/String;
-Lcom/android/ims/internal/uce/presence/PresCapInfo;->mContactUri:Ljava/lang/String;
-Lcom/android/ims/internal/uce/presence/PresServiceInfo;->getMediaType()I
-Lcom/android/ims/internal/uce/presence/PresServiceInfo;->getServiceDesc()Ljava/lang/String;
-Lcom/android/ims/internal/uce/presence/PresServiceInfo;->getServiceId()Ljava/lang/String;
-Lcom/android/ims/internal/uce/presence/PresServiceInfo;->getServiceVer()Ljava/lang/String;
-Lcom/android/ims/internal/uce/presence/PresSipResponse;->getCmdId()Lcom/android/ims/internal/uce/presence/PresCmdId;
-Lcom/android/ims/internal/uce/presence/PresSipResponse;->getReasonPhrase()Ljava/lang/String;
-Lcom/android/ims/internal/uce/presence/PresSipResponse;->getRequestId()I
-Lcom/android/ims/internal/uce/presence/PresSipResponse;->getRetryAfter()I
-Lcom/android/ims/internal/uce/presence/PresSipResponse;->getSipResponseCode()I
 Lcom/android/ims/internal/uce/uceservice/IUceListener;->setStatus(I)V
 Lcom/android/ims/internal/uce/uceservice/IUceService$Stub;-><init>()V
 Lcom/android/ims/internal/uce/uceservice/IUceService;->createOptionsService(Lcom/android/ims/internal/uce/options/IOptionsListener;Lcom/android/ims/internal/uce/common/UceLong;)I
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index 300a530..5b73eaa 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -21,6 +21,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
+import android.annotation.UnsupportedAppUsage;
 import android.app.Service;
 import android.content.Context;
 import android.content.Intent;
@@ -442,8 +443,10 @@
 
     private int mConnectionId = AccessibilityInteractionClient.NO_ID;
 
+    @UnsupportedAppUsage
     private AccessibilityServiceInfo mInfo;
 
+    @UnsupportedAppUsage
     private IBinder mWindowToken;
 
     private WindowManager mWindowManager;
diff --git a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
index ed684d7..be2e2fa 100644
--- a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
+++ b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
@@ -19,6 +19,7 @@
 import static android.content.pm.PackageManager.FEATURE_FINGERPRINT;
 
 import android.annotation.IntDef;
+import android.annotation.UnsupportedAppUsage;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.pm.PackageManager;
@@ -695,6 +696,7 @@
      *
      * @hide
      */
+    @UnsupportedAppUsage
     public void setCapabilities(int capabilities) {
         mCapabilities = capabilities;
     }
diff --git a/core/java/android/animation/Animator.java b/core/java/android/animation/Animator.java
index 4ebcc44..17d54d2 100644
--- a/core/java/android/animation/Animator.java
+++ b/core/java/android/animation/Animator.java
@@ -17,6 +17,7 @@
 package android.animation;
 
 import android.annotation.Nullable;
+import android.annotation.UnsupportedAppUsage;
 import android.content.pm.ActivityInfo.Config;
 import android.content.res.ConstantState;
 
@@ -460,6 +461,7 @@
     /**
      * @hide
      */
+    @UnsupportedAppUsage
     public void reverse() {
         throw new IllegalStateException("Reverse is not supported");
     }
diff --git a/core/java/android/animation/ArgbEvaluator.java b/core/java/android/animation/ArgbEvaluator.java
index a96bee6..5b69d18 100644
--- a/core/java/android/animation/ArgbEvaluator.java
+++ b/core/java/android/animation/ArgbEvaluator.java
@@ -16,6 +16,8 @@
 
 package android.animation;
 
+import android.annotation.UnsupportedAppUsage;
+
 /**
  * This evaluator can be used to perform type interpolation between integer
  * values that represent ARGB colors.
@@ -31,6 +33,7 @@
      *
      * @hide
      */
+    @UnsupportedAppUsage
     public static ArgbEvaluator getInstance() {
         return sInstance;
     }
diff --git a/core/java/android/animation/LayoutTransition.java b/core/java/android/animation/LayoutTransition.java
index 5a23fdd..5b3813d 100644
--- a/core/java/android/animation/LayoutTransition.java
+++ b/core/java/android/animation/LayoutTransition.java
@@ -16,6 +16,7 @@
 
 package android.animation;
 
+import android.annotation.UnsupportedAppUsage;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewParent;
@@ -1070,6 +1071,7 @@
      *
      * @hide
      */
+    @UnsupportedAppUsage
     public void cancel() {
         if (currentChangingAnimations.size() > 0) {
             LinkedHashMap<View, Animator> currentAnimCopy =
@@ -1105,6 +1107,7 @@
      *
      * @hide
      */
+    @UnsupportedAppUsage
     public void cancel(int transitionType) {
         switch (transitionType) {
             case CHANGE_APPEARING:
diff --git a/core/java/android/animation/ValueAnimator.java b/core/java/android/animation/ValueAnimator.java
index cc95eb6..a0464df 100644
--- a/core/java/android/animation/ValueAnimator.java
+++ b/core/java/android/animation/ValueAnimator.java
@@ -19,6 +19,7 @@
 import android.annotation.CallSuper;
 import android.annotation.IntDef;
 import android.annotation.TestApi;
+import android.annotation.UnsupportedAppUsage;
 import android.os.Looper;
 import android.os.Trace;
 import android.util.AndroidRuntimeException;
@@ -75,6 +76,7 @@
     /**
      * Internal constants
      */
+    @UnsupportedAppUsage
     private static float sDurationScale = 1.0f;
 
     /**
@@ -200,6 +202,7 @@
     //
 
     // How long the animation should last in ms
+    @UnsupportedAppUsage
     private long mDuration = 300;
 
     // The amount of time in ms to delay starting the animation after start() is called. Note
@@ -1534,6 +1537,7 @@
      * @param fraction The elapsed fraction of the animation.
      */
     @CallSuper
+    @UnsupportedAppUsage
     void animateValue(float fraction) {
         fraction = mInterpolator.getInterpolation(fraction);
         mCurrentFraction = fraction;
diff --git a/core/java/android/app/ActivityTaskManager.java b/core/java/android/app/ActivityTaskManager.java
index 398644af..2f18b89 100644
--- a/core/java/android/app/ActivityTaskManager.java
+++ b/core/java/android/app/ActivityTaskManager.java
@@ -204,6 +204,19 @@
     }
 
     /**
+     * Removes all visible recent tasks from the system.
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.REMOVE_TASKS)
+    public void removeAllVisibleRecentTasks() {
+        try {
+            getService().removeAllVisibleRecentTasks();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Return the maximum number of recents entries that we will maintain and show.
      * @hide
      */
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index ece7f83..46664c6 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -139,6 +139,7 @@
     ComponentName getCallingActivity(in IBinder token);
     void setFocusedTask(int taskId);
     boolean removeTask(int taskId);
+    void removeAllVisibleRecentTasks();
     List<ActivityManager.RunningTaskInfo> getTasks(int maxNum);
     List<ActivityManager.RunningTaskInfo> getFilteredTasks(int maxNum, int ignoreActivityType,
             int ignoreWindowingMode);
diff --git a/core/java/android/database/sqlite/SQLiteConnection.java b/core/java/android/database/sqlite/SQLiteConnection.java
index df6447a..3597260 100644
--- a/core/java/android/database/sqlite/SQLiteConnection.java
+++ b/core/java/android/database/sqlite/SQLiteConnection.java
@@ -19,6 +19,7 @@
 import android.database.Cursor;
 import android.database.CursorWindow;
 import android.database.DatabaseUtils;
+import android.database.sqlite.SQLiteDebug.Consts;
 import android.database.sqlite.SQLiteDebug.DbStats;
 import android.os.CancellationSignal;
 import android.os.OperationCanceledException;
@@ -34,7 +35,6 @@
 
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Date;
 import java.util.Map;
 
@@ -90,8 +90,6 @@
     private static final String TAG = "SQLiteConnection";
     private static final boolean DEBUG = false;
 
-    public static volatile boolean sLocalDebug = false;
-
     private static final String[] EMPTY_STRING_ARRAY = new String[0];
     private static final byte[] EMPTY_BYTE_ARRAY = new byte[0];
 
@@ -212,7 +210,7 @@
     private void open() {
         mConnectionPtr = nativeOpen(mConfiguration.path, mConfiguration.openFlags,
                 mConfiguration.label,
-                SQLiteDebug.DEBUG_SQL_STATEMENTS, SQLiteDebug.DEBUG_SQL_TIME,
+                SQLiteDebug.Consts.DEBUG_SQL_STATEMENTS, SQLiteDebug.Consts.DEBUG_SQL_TIME,
                 mConfiguration.lookasideSlotSize, mConfiguration.lookasideSlotCount);
         setPageSize();
         setForeignKeyModeFromConfiguration();
@@ -993,10 +991,6 @@
     }
 
     private void bindArguments(PreparedStatement statement, Object[] bindArgs) {
-        if (sLocalDebug) {
-            Log.v(TAG, statement.mSql + " with args " + Arrays.toString(bindArgs));
-        }
-
         final int count = bindArgs != null ? bindArgs.length : 0;
         if (count != statement.mNumParameters) {
             throw new SQLiteBindOrColumnIndexOutOfRangeException(
@@ -1097,7 +1091,7 @@
         printer.println("  isPrimaryConnection: " + mIsPrimaryConnection);
         printer.println("  onlyAllowReadOnlyOperations: " + mOnlyAllowReadOnlyOperations);
 
-        mRecentOperations.dump(printer, verbose);
+        mRecentOperations.dump(printer);
 
         if (verbose) {
             mPreparedStatementCache.dump(printer);
@@ -1407,7 +1401,7 @@
                 operation.mFinished = true;
                 final long execTime = operation.mEndTime - operation.mStartTime;
                 mPool.onStatementExecuted(execTime);
-                return SQLiteDebug.DEBUG_LOG_SLOW_QUERIES && SQLiteDebug.shouldLogSlowQuery(
+                return SQLiteDebug.Consts.DEBUG_LOG_SLOW_QUERIES && SQLiteDebug.shouldLogSlowQuery(
                         execTime);
             }
             return false;
@@ -1416,7 +1410,7 @@
         private void logOperationLocked(int cookie, String detail) {
             final Operation operation = getOperationLocked(cookie);
             StringBuilder msg = new StringBuilder();
-            operation.describe(msg, false);
+            operation.describe(msg, true);
             if (detail != null) {
                 msg.append(", ").append(detail);
             }
@@ -1446,7 +1440,7 @@
             }
         }
 
-        public void dump(Printer printer, boolean verbose) {
+        public void dump(Printer printer) {
             synchronized (mOperations) {
                 printer.println("  Most recently executed operations:");
                 int index = mIndex;
@@ -1463,7 +1457,7 @@
                         String formattedStartTime = opDF.format(new Date(operation.mStartWallTime));
                         msg.append(formattedStartTime);
                         msg.append("] ");
-                        operation.describe(msg, verbose);
+                        operation.describe(msg, false); // Never dump bingargs in a bugreport
                         printer.println(msg.toString());
 
                         if (index > 0) {
@@ -1498,7 +1492,7 @@
         public Exception mException;
         public int mCookie;
 
-        public void describe(StringBuilder msg, boolean verbose) {
+        public void describe(StringBuilder msg, boolean allowBindArgsLog) {
             msg.append(mKind);
             if (mFinished) {
                 msg.append(" took ").append(mEndTime - mStartTime).append("ms");
@@ -1510,7 +1504,8 @@
             if (mSql != null) {
                 msg.append(", sql=\"").append(trimSqlForDisplay(mSql)).append("\"");
             }
-            if (verbose && mBindArgs != null && mBindArgs.size() != 0) {
+            if (allowBindArgsLog && Consts.DEBUG_LOG_BIND_ARGS
+                    && mBindArgs != null && mBindArgs.size() != 0) {
                 msg.append(", bindArgs=[");
                 final int count = mBindArgs.size();
                 for (int i = 0; i < count; i++) {
diff --git a/core/java/android/database/sqlite/SQLiteDebug.java b/core/java/android/database/sqlite/SQLiteDebug.java
index d392521..790af6a 100644
--- a/core/java/android/database/sqlite/SQLiteDebug.java
+++ b/core/java/android/database/sqlite/SQLiteDebug.java
@@ -18,6 +18,7 @@
 
 import android.annotation.TestApi;
 import android.os.Build;
+import android.os.Process;
 import android.os.SystemProperties;
 import android.util.Log;
 import android.util.Printer;
@@ -34,35 +35,53 @@
     private static native void nativeGetPagerStats(PagerStats stats);
 
     /**
-     * Controls the printing of informational SQL log messages.
+     * Inner class to avoid getting the value frozen in zygote.
      *
-     * Enable using "adb shell setprop log.tag.SQLiteLog VERBOSE".
+     * {@hide}
      */
-    public static final boolean DEBUG_SQL_LOG =
-            Log.isLoggable("SQLiteLog", Log.VERBOSE);
+    public static final class Consts {
+        /**
+         * Controls the printing of informational SQL log messages.
+         *
+         * Enable using "adb shell setprop log.tag.SQLiteLog VERBOSE".
+         */
+        public static final boolean DEBUG_SQL_LOG =
+                Log.isLoggable("SQLiteLog", Log.VERBOSE);
 
-    /**
-     * Controls the printing of SQL statements as they are executed.
-     *
-     * Enable using "adb shell setprop log.tag.SQLiteStatements VERBOSE".
-     */
-    public static final boolean DEBUG_SQL_STATEMENTS =
-            Log.isLoggable("SQLiteStatements", Log.VERBOSE);
+        /**
+         * Controls the printing of SQL statements as they are executed.
+         *
+         * Enable using "adb shell setprop log.tag.SQLiteStatements VERBOSE".
+         */
+        public static final boolean DEBUG_SQL_STATEMENTS =
+                Log.isLoggable("SQLiteStatements", Log.VERBOSE);
 
-    /**
-     * Controls the printing of wall-clock time taken to execute SQL statements
-     * as they are executed.
-     *
-     * Enable using "adb shell setprop log.tag.SQLiteTime VERBOSE".
-     */
-    public static final boolean DEBUG_SQL_TIME =
-            Log.isLoggable("SQLiteTime", Log.VERBOSE);
+        /**
+         * Controls the printing of wall-clock time taken to execute SQL statements
+         * as they are executed.
+         *
+         * Enable using "adb shell setprop log.tag.SQLiteTime VERBOSE".
+         */
+        public static final boolean DEBUG_SQL_TIME =
+                Log.isLoggable("SQLiteTime", Log.VERBOSE);
 
-    /**
-     * True to enable database performance testing instrumentation.
-     * @hide
-     */
-    public static final boolean DEBUG_LOG_SLOW_QUERIES = Build.IS_DEBUGGABLE;
+
+        /**
+         * True to enable database performance testing instrumentation.
+         */
+        public static final boolean DEBUG_LOG_SLOW_QUERIES = Build.IS_DEBUGGABLE;
+
+        private static final String SLOW_QUERY_THRESHOLD_PROP = "db.log.slow_query_threshold";
+
+        private static final String SLOW_QUERY_THRESHOLD_UID_PROP =
+                SLOW_QUERY_THRESHOLD_PROP + "." + Process.myUid();
+
+        /**
+         * Whether to log bind args in slow query log or not.
+         */
+        public static final boolean DEBUG_LOG_BIND_ARGS = Build.IS_DEBUGGABLE
+                && SystemProperties.getBoolean("db.log.bindargs", false);
+    }
 
     private SQLiteDebug() {
     }
@@ -75,14 +94,19 @@
      * be considered slow.  If the value does not exist or is negative, then no queries will
      * be considered slow.
      *
+     * To enable it for a specific UID, "db.log.slow_query_threshold.UID" could also be used.
+     *
      * This value can be changed dynamically while the system is running.
      * For example, "adb shell setprop db.log.slow_query_threshold 200" will
      * log all queries that take 200ms or longer to run.
      * @hide
      */
-    public static final boolean shouldLogSlowQuery(long elapsedTimeMillis) {
-        int slowQueryMillis = SystemProperties.getInt("db.log.slow_query_threshold", -1);
-        return slowQueryMillis >= 0 && elapsedTimeMillis >= slowQueryMillis;
+    public static boolean shouldLogSlowQuery(long elapsedTimeMillis) {
+        final int slowQueryMillis = Math.min(
+                SystemProperties.getInt(Consts.SLOW_QUERY_THRESHOLD_PROP, Integer.MAX_VALUE),
+                SystemProperties.getInt(Consts.SLOW_QUERY_THRESHOLD_UID_PROP,
+                        Integer.MAX_VALUE));
+        return elapsedTimeMillis >= slowQueryMillis;
     }
 
     /**
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 431c651..46671b2 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -28,6 +28,7 @@
 import android.annotation.MainThread;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.UnsupportedAppUsage;
 import android.app.ActivityManager;
 import android.app.Dialog;
 import android.content.Context;
@@ -343,10 +344,12 @@
 
     InputMethodManager mImm;
     
+    @UnsupportedAppUsage
     int mTheme = 0;
     
     LayoutInflater mInflater;
     TypedArray mThemeAttrs;
+    @UnsupportedAppUsage
     View mRootView;
     SoftInputWindow mWindow;
     boolean mInitialized;
@@ -378,8 +381,10 @@
 
     boolean mFullscreenApplied;
     boolean mIsFullscreen;
+    @UnsupportedAppUsage
     View mExtractView;
     boolean mExtractViewHidden;
+    @UnsupportedAppUsage
     ExtractEditText mExtractEditText;
     ViewGroup mExtractAccessories;
     View mExtractAction;
@@ -402,6 +407,7 @@
      */
     boolean mShouldClearInsetOfPreviousIme;
 
+    @UnsupportedAppUsage
     final Insets mTmpInsets = new Insets();
     final int[] mTmpLocation = new int[2];
 
@@ -811,6 +817,7 @@
             mService.getContentResolver().unregisterContentObserver(this);
         }
 
+        @UnsupportedAppUsage
         private boolean shouldShowImeWithHardKeyboard() {
             // Lazily initialize as needed.
             if (mShowImeWithHardKeyboard == ShowImeWithHardKeyboardType.UNKNOWN) {
@@ -850,6 +857,7 @@
             return "SettingsObserver{mShowImeWithHardKeyboard=" + mShowImeWithHardKeyboard  + "}";
         }
     }
+    @UnsupportedAppUsage
     private SettingsObserver mSettingsObserver;
 
     /**
@@ -2492,6 +2500,7 @@
     /**
      * @hide
      */
+    @UnsupportedAppUsage
     public void onExtractedDeleteText(int start, int end) {
         InputConnection conn = getCurrentInputConnection();
         if (conn != null) {
@@ -2504,6 +2513,7 @@
     /**
      * @hide
      */
+    @UnsupportedAppUsage
     public void onExtractedReplaceText(int start, int end, CharSequence text) {
         InputConnection conn = getCurrentInputConnection();
         if (conn != null) {
@@ -2515,6 +2525,7 @@
     /**
      * @hide
      */
+    @UnsupportedAppUsage
     public void onExtractedSetSpan(Object span, int start, int end, int flags) {
         InputConnection conn = getCurrentInputConnection();
         if (conn != null) {
diff --git a/core/java/android/inputmethodservice/Keyboard.java b/core/java/android/inputmethodservice/Keyboard.java
index a5490ef..ec5f050 100644
--- a/core/java/android/inputmethodservice/Keyboard.java
+++ b/core/java/android/inputmethodservice/Keyboard.java
@@ -18,6 +18,7 @@
 
 import org.xmlpull.v1.XmlPullParserException;
 
+import android.annotation.UnsupportedAppUsage;
 import android.annotation.XmlRes;
 import android.content.Context;
 import android.content.res.Resources;
@@ -110,18 +111,21 @@
     private int mKeyHeight;
     
     /** Total height of the keyboard, including the padding and keys */
+    @UnsupportedAppUsage
     private int mTotalHeight;
     
     /** 
      * Total width of the keyboard, including left side gaps and keys, but not any gaps on the
      * right side.
      */
+    @UnsupportedAppUsage
     private int mTotalWidth;
     
     /** List of keys in this keyboard */
     private List<Key> mKeys;
     
     /** List of modifier keys such as Shift & Alt, if any */
+    @UnsupportedAppUsage
     private List<Key> mModifierKeys;
     
     /** Width of the screen available to fit the keyboard */
@@ -623,6 +627,7 @@
         rows.add(row);
     }
 
+    @UnsupportedAppUsage
     final void resize(int newWidth, int newHeight) {
         int numRows = rows.size();
         for (int rowIndex = 0; rowIndex < numRows; ++rowIndex) {
diff --git a/core/java/android/inputmethodservice/KeyboardView.java b/core/java/android/inputmethodservice/KeyboardView.java
index 16c1f6d..9ca8049 100644
--- a/core/java/android/inputmethodservice/KeyboardView.java
+++ b/core/java/android/inputmethodservice/KeyboardView.java
@@ -16,6 +16,7 @@
 
 package android.inputmethodservice;
 
+import android.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.graphics.Bitmap;
@@ -133,6 +134,7 @@
 
     private Keyboard mKeyboard;
     private int mCurrentKeyIndex = NOT_A_KEY;
+    @UnsupportedAppUsage
     private int mLabelTextSize;
     private int mKeyTextSize;
     private int mKeyTextColor;
@@ -140,6 +142,7 @@
     private int mShadowColor;
     private float mBackgroundDimAmount;
 
+    @UnsupportedAppUsage
     private TextView mPreviewText;
     private PopupWindow mPreviewPopup;
     private int mPreviewTextSizeLarge;
@@ -217,6 +220,7 @@
     private float mOldPointerX;
     private float mOldPointerY;
 
+    @UnsupportedAppUsage
     private Drawable mKeyBackground;
 
     private static final int REPEAT_INTERVAL = 50; // ~20 keys per second
@@ -910,6 +914,7 @@
         }
     }
 
+    @UnsupportedAppUsage
     private void showKey(final int keyIndex) {
         final PopupWindow previewPopup = mPreviewPopup;
         final Key[] keys = mKeys;
@@ -1052,6 +1057,7 @@
                 key.x + key.width + mPaddingLeft, key.y + key.height + mPaddingTop);
     }
 
+    @UnsupportedAppUsage
     private boolean openPopupIfRequired(MotionEvent me) {
         // Check if we have a popup layout specified first.
         if (mPopupLayout == 0) {
@@ -1357,6 +1363,7 @@
         return true;
     }
 
+    @UnsupportedAppUsage
     private boolean repeatKey() {
         Key key = mKeys[mRepeatKeyIndex];
         detectAndSendKey(mCurrentKey, key.x, key.y, mLastTapTime);
diff --git a/core/java/android/nfc/ErrorCodes.java b/core/java/android/nfc/ErrorCodes.java
index 3adcdc3..98e31ad 100644
--- a/core/java/android/nfc/ErrorCodes.java
+++ b/core/java/android/nfc/ErrorCodes.java
@@ -16,6 +16,8 @@
 
 package android.nfc;
 
+import android.annotation.UnsupportedAppUsage;
+
 /**
  * This class defines all the error codes that can be returned by the service
  * and producing an exception on the application level. These are needed since
@@ -25,6 +27,7 @@
  */
 public class ErrorCodes {
 
+    @UnsupportedAppUsage
     public static boolean isError(int code) {
         if (code < 0) {
             return true;
diff --git a/core/java/android/nfc/NdefRecord.java b/core/java/android/nfc/NdefRecord.java
index 093a9b4..b0090ca 100644
--- a/core/java/android/nfc/NdefRecord.java
+++ b/core/java/android/nfc/NdefRecord.java
@@ -16,6 +16,7 @@
 
 package android.nfc;
 
+import android.annotation.UnsupportedAppUsage;
 import android.content.Intent;
 import android.net.Uri;
 import android.os.Parcel;
@@ -279,6 +280,7 @@
 
     private final short mTnf;
     private final byte[] mType;
+    @UnsupportedAppUsage
     private final byte[] mId;
     private final byte[] mPayload;
 
diff --git a/core/java/android/nfc/NfcActivityManager.java b/core/java/android/nfc/NfcActivityManager.java
index 958063a..abfa133 100644
--- a/core/java/android/nfc/NfcActivityManager.java
+++ b/core/java/android/nfc/NfcActivityManager.java
@@ -16,6 +16,7 @@
 
 package android.nfc;
 
+import android.annotation.UnsupportedAppUsage;
 import android.app.Activity;
 import android.app.Application;
 import android.content.ContentProvider;
@@ -45,6 +46,7 @@
     static final String TAG = NfcAdapter.TAG;
     static final Boolean DBG = false;
 
+    @UnsupportedAppUsage
     final NfcAdapter mAdapter;
 
     // All objects in the lists are protected by this
diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java
index c3f23a1..21fed48 100644
--- a/core/java/android/nfc/NfcAdapter.java
+++ b/core/java/android/nfc/NfcAdapter.java
@@ -20,6 +20,7 @@
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
 import android.annotation.SystemApi;
+import android.annotation.UnsupportedAppUsage;
 import android.app.Activity;
 import android.app.ActivityThread;
 import android.app.OnActivityPausedListener;
@@ -325,6 +326,7 @@
     // Final after first constructor, except for
     // attemptDeadServiceRecovery() when NFC crashes - we accept a best effort
     // recovery
+    @UnsupportedAppUsage
     static INfcAdapter sService;
     static INfcTag sTagService;
     static INfcCardEmulation sCardEmulationService;
@@ -490,6 +492,7 @@
      * or throws if NFC is not available.
      * @hide
      */
+    @UnsupportedAppUsage
     public static synchronized NfcAdapter getNfcAdapter(Context context) {
         if (!sIsInitialized) {
             sHasNfcFeature = hasNfcFeature();
@@ -593,6 +596,7 @@
      * @hide
      */
     @Deprecated
+    @UnsupportedAppUsage
     public static NfcAdapter getDefaultAdapter() {
         // introduced in API version 9 (GB 2.3)
         // deprecated in API version 10 (GB 2.3.3)
@@ -615,6 +619,7 @@
     /**
      * @hide
      */
+    @UnsupportedAppUsage
     public Context getContext() {
         return mContext;
     }
@@ -623,6 +628,7 @@
      * Returns the binder interface to the service.
      * @hide
      */
+    @UnsupportedAppUsage
     public INfcAdapter getService() {
         isEnabled();  // NOP call to recover sService if it is stale
         return sService;
@@ -676,6 +682,7 @@
      * NFC service dead - attempt best effort recovery
      * @hide
      */
+    @UnsupportedAppUsage
     public void attemptDeadServiceRecovery(Exception e) {
         Log.e(TAG, "NFC service dead - attempting to recover", e);
         INfcAdapter service = getServiceInterface();
@@ -746,6 +753,7 @@
      *
      * @hide
      */
+    @UnsupportedAppUsage
     public int getAdapterState() {
         try {
             return sService.getState();
@@ -1227,6 +1235,7 @@
     /**
      * @hide
      */
+    @UnsupportedAppUsage
     public void setNdefPushMessageCallback(CreateNdefMessageCallback callback, Activity activity,
             int flags) {
         if (activity == null) {
@@ -1862,6 +1871,7 @@
     /**
      * @hide
      */
+    @UnsupportedAppUsage
     public INfcAdapterExtras getNfcAdapterExtrasInterface() {
         if (mContext == null) {
             throw new UnsupportedOperationException("You need a context on NfcAdapter to use the "
diff --git a/core/java/android/nfc/NfcManager.java b/core/java/android/nfc/NfcManager.java
index 50d6745..71199c9 100644
--- a/core/java/android/nfc/NfcManager.java
+++ b/core/java/android/nfc/NfcManager.java
@@ -17,6 +17,7 @@
 package android.nfc;
 
 import android.annotation.SystemService;
+import android.annotation.UnsupportedAppUsage;
 import android.content.Context;
 
 /**
@@ -44,6 +45,7 @@
     /**
      * @hide
      */
+    @UnsupportedAppUsage
     public NfcManager(Context context) {
         NfcAdapter adapter;
         context = context.getApplicationContext();
diff --git a/core/java/android/nfc/Tag.java b/core/java/android/nfc/Tag.java
index 154d5a1..ce684cf 100644
--- a/core/java/android/nfc/Tag.java
+++ b/core/java/android/nfc/Tag.java
@@ -16,6 +16,7 @@
 
 package android.nfc;
 
+import android.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.nfc.tech.IsoDep;
 import android.nfc.tech.MifareClassic;
@@ -110,6 +111,7 @@
  * <p>
  */
 public final class Tag implements Parcelable {
+    @UnsupportedAppUsage
     final byte[] mId;
     final int[] mTechList;
     final String[] mTechStringList;
@@ -235,6 +237,7 @@
      * For use by NfcService only.
      * @hide
      */
+    @UnsupportedAppUsage
     public int getServiceHandle() {
         return mServiceHandle;
     }
@@ -355,6 +358,7 @@
     }
 
     /** @hide */
+    @UnsupportedAppUsage
     public INfcTag getTagService() {
         return mTagService;
     }
diff --git a/core/java/android/nfc/cardemulation/AidGroup.java b/core/java/android/nfc/cardemulation/AidGroup.java
index 78a9401..63776c4 100644
--- a/core/java/android/nfc/cardemulation/AidGroup.java
+++ b/core/java/android/nfc/cardemulation/AidGroup.java
@@ -24,6 +24,7 @@
 import org.xmlpull.v1.XmlPullParserException;
 import org.xmlpull.v1.XmlSerializer;
 
+import android.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.Log;
@@ -45,8 +46,11 @@
 
     static final String TAG = "AidGroup";
 
+    @UnsupportedAppUsage
     final List<String> aids;
+    @UnsupportedAppUsage
     final String category;
+    @UnsupportedAppUsage
     final String description;
 
     /**
@@ -79,6 +83,7 @@
         this.description = null;
     }
 
+    @UnsupportedAppUsage
     AidGroup(String category, String description) {
         this.aids = new ArrayList<String>();
         this.category = category;
@@ -88,6 +93,7 @@
     /**
      * @return the category of this AID group
      */
+    @UnsupportedAppUsage
     public String getCategory() {
         return category;
     }
@@ -95,6 +101,7 @@
     /**
      * @return the list of AIDs in this group
      */
+    @UnsupportedAppUsage
     public List<String> getAids() {
         return aids;
     }
@@ -124,6 +131,7 @@
         }
     }
 
+    @UnsupportedAppUsage
     public static final Parcelable.Creator<AidGroup> CREATOR =
             new Parcelable.Creator<AidGroup>() {
 
@@ -144,6 +152,7 @@
         }
     };
 
+    @UnsupportedAppUsage
     static public AidGroup createFromXml(XmlPullParser parser) throws XmlPullParserException, IOException {
         String category = null;
         ArrayList<String> aids = new ArrayList<String>();
@@ -185,6 +194,7 @@
         return group;
     }
 
+    @UnsupportedAppUsage
     public void writeAsXml(XmlSerializer out) throws IOException {
         out.startTag(null, "aid-group");
         out.attribute(null, "category", category);
diff --git a/core/java/android/nfc/cardemulation/ApduServiceInfo.java b/core/java/android/nfc/cardemulation/ApduServiceInfo.java
index 218e4f2..e8d801c 100644
--- a/core/java/android/nfc/cardemulation/ApduServiceInfo.java
+++ b/core/java/android/nfc/cardemulation/ApduServiceInfo.java
@@ -16,6 +16,7 @@
 
 package android.nfc.cardemulation;
 
+import android.annotation.UnsupportedAppUsage;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.pm.PackageManager;
@@ -54,6 +55,7 @@
     /**
      * The service that implements this
      */
+    @UnsupportedAppUsage
     final ResolveInfo mService;
 
     /**
@@ -69,11 +71,13 @@
     /**
      * Mapping from category to static AID group
      */
+    @UnsupportedAppUsage
     final HashMap<String, AidGroup> mStaticAidGroups;
 
     /**
      * Mapping from category to dynamic AID group
      */
+    @UnsupportedAppUsage
     final HashMap<String, AidGroup> mDynamicAidGroups;
 
     /**
@@ -99,6 +103,7 @@
     /**
      * @hide
      */
+    @UnsupportedAppUsage
     public ApduServiceInfo(ResolveInfo info, boolean onHost, String description,
             ArrayList<AidGroup> staticAidGroups, ArrayList<AidGroup> dynamicAidGroups,
             boolean requiresUnlock, int bannerResource, int uid,
@@ -120,6 +125,7 @@
         this.mSettingsActivityName = settingsActivityName;
     }
 
+    @UnsupportedAppUsage
     public ApduServiceInfo(PackageManager pm, ResolveInfo info, boolean onHost) throws
             XmlPullParserException, IOException {
         ServiceInfo si = info.serviceInfo;
@@ -374,18 +380,22 @@
         return (mStaticAidGroups.containsKey(category) || mDynamicAidGroups.containsKey(category));
     }
 
+    @UnsupportedAppUsage
     public boolean isOnHost() {
         return mOnHost;
     }
 
+    @UnsupportedAppUsage
     public boolean requiresUnlock() {
         return mRequiresDeviceUnlock;
     }
 
+    @UnsupportedAppUsage
     public String getDescription() {
         return mDescription;
     }
 
+    @UnsupportedAppUsage
     public int getUid() {
         return mUid;
     }
@@ -411,6 +421,7 @@
         return mService.loadIcon(pm);
     }
 
+    @UnsupportedAppUsage
     public Drawable loadBanner(PackageManager pm) {
         Resources res;
         try {
@@ -426,6 +437,7 @@
         }
     }
 
+    @UnsupportedAppUsage
     public String getSettingsActivityName() { return mSettingsActivityName; }
 
     @Override
@@ -483,6 +495,7 @@
         dest.writeString(mSettingsActivityName);
     };
 
+    @UnsupportedAppUsage
     public static final Parcelable.Creator<ApduServiceInfo> CREATOR =
             new Parcelable.Creator<ApduServiceInfo>() {
         @Override
diff --git a/core/java/android/service/notification/Adjustment.java b/core/java/android/service/notification/Adjustment.java
index 0d94af4..e0c354a 100644
--- a/core/java/android/service/notification/Adjustment.java
+++ b/core/java/android/service/notification/Adjustment.java
@@ -71,6 +71,12 @@
     public static final String KEY_SMART_ACTIONS = "key_smart_actions";
 
     /**
+     * Data type: ArrayList of {@link CharSequence}.
+     * Used to suggest smart replies for a notification.
+     */
+    public static final String KEY_SMART_REPLIES = "key_smart_replies";
+
+    /**
      * Create a notification adjustment.
      *
      * @param pkg The package of the notification.
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index 09425a9..09eecd8 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -1427,6 +1427,7 @@
         private @UserSentiment int mUserSentiment = USER_SENTIMENT_NEUTRAL;
         private boolean mHidden;
         private ArrayList<Notification.Action> mSmartActions;
+        private ArrayList<CharSequence> mSmartReplies;
 
         public Ranking() {}
 
@@ -1564,6 +1565,13 @@
         }
 
         /**
+         * @hide
+         */
+        public List<CharSequence> getSmartReplies() {
+            return mSmartReplies;
+        }
+
+        /**
          * Returns whether this notification can be displayed as a badge.
          *
          * @return true if the notification can be displayed as a badge, false otherwise.
@@ -1591,7 +1599,8 @@
                 CharSequence explanation, String overrideGroupKey,
                 NotificationChannel channel, ArrayList<String> overridePeople,
                 ArrayList<SnoozeCriterion> snoozeCriteria, boolean showBadge,
-                int userSentiment, boolean hidden, ArrayList<Notification.Action> smartActions) {
+                int userSentiment, boolean hidden, ArrayList<Notification.Action> smartActions,
+                ArrayList<CharSequence> smartReplies) {
             mKey = key;
             mRank = rank;
             mIsAmbient = importance < NotificationManager.IMPORTANCE_LOW;
@@ -1608,6 +1617,7 @@
             mUserSentiment = userSentiment;
             mHidden = hidden;
             mSmartActions = smartActions;
+            mSmartReplies = smartReplies;
         }
 
         /**
@@ -1658,6 +1668,7 @@
         private ArrayMap<String, Integer> mUserSentiment;
         private ArrayMap<String, Boolean> mHidden;
         private ArrayMap<String, ArrayList<Notification.Action>> mSmartActions;
+        private ArrayMap<String, ArrayList<CharSequence>> mSmartReplies;
 
         private RankingMap(NotificationRankingUpdate rankingUpdate) {
             mRankingUpdate = rankingUpdate;
@@ -1686,7 +1697,8 @@
                     getVisibilityOverride(key), getSuppressedVisualEffects(key),
                     getImportance(key), getImportanceExplanation(key), getOverrideGroupKey(key),
                     getChannel(key), getOverridePeople(key), getSnoozeCriteria(key),
-                    getShowBadge(key), getUserSentiment(key), getHidden(key), getSmartActions(key));
+                    getShowBadge(key), getUserSentiment(key), getHidden(key), getSmartActions(key),
+                    getSmartReplies(key));
             return rank >= 0;
         }
 
@@ -1833,6 +1845,15 @@
             return mSmartActions.get(key);
         }
 
+        private ArrayList<CharSequence> getSmartReplies(String key) {
+            synchronized (this) {
+                if (mSmartReplies == null) {
+                    buildSmartReplies();
+                }
+            }
+            return mSmartReplies.get(key);
+        }
+
         // Locked by 'this'
         private void buildRanksLocked() {
             String[] orderedKeys = mRankingUpdate.getOrderedKeys();
@@ -1959,6 +1980,15 @@
             }
         }
 
+        // Locked by 'this'
+        private void buildSmartReplies() {
+            Bundle smartReplies = mRankingUpdate.getSmartReplies();
+            mSmartReplies = new ArrayMap<>(smartReplies.size());
+            for (String key : smartReplies.keySet()) {
+                mSmartReplies.put(key, smartReplies.getCharSequenceArrayList(key));
+            }
+        }
+
         // ----------- Parcelable
 
         @Override
diff --git a/core/java/android/service/notification/NotificationRankingUpdate.java b/core/java/android/service/notification/NotificationRankingUpdate.java
index bed22149..c67fad0 100644
--- a/core/java/android/service/notification/NotificationRankingUpdate.java
+++ b/core/java/android/service/notification/NotificationRankingUpdate.java
@@ -38,12 +38,14 @@
     private final Bundle mUserSentiment;
     private final Bundle mHidden;
     private final Bundle mSmartActions;
+    private final Bundle mSmartReplies;
 
     public NotificationRankingUpdate(String[] keys, String[] interceptedKeys,
             Bundle visibilityOverrides, Bundle suppressedVisualEffects,
             int[] importance, Bundle explanation, Bundle overrideGroupKeys,
             Bundle channels, Bundle overridePeople, Bundle snoozeCriteria,
-            Bundle showBadge, Bundle userSentiment, Bundle hidden, Bundle smartActions) {
+            Bundle showBadge, Bundle userSentiment, Bundle hidden, Bundle smartActions,
+            Bundle smartReplies) {
         mKeys = keys;
         mInterceptedKeys = interceptedKeys;
         mVisibilityOverrides = visibilityOverrides;
@@ -58,6 +60,7 @@
         mUserSentiment = userSentiment;
         mHidden = hidden;
         mSmartActions = smartActions;
+        mSmartReplies = smartReplies;
     }
 
     public NotificationRankingUpdate(Parcel in) {
@@ -76,6 +79,7 @@
         mUserSentiment = in.readBundle();
         mHidden = in.readBundle();
         mSmartActions = in.readBundle();
+        mSmartReplies = in.readBundle();
     }
 
     @Override
@@ -99,6 +103,7 @@
         out.writeBundle(mUserSentiment);
         out.writeBundle(mHidden);
         out.writeBundle(mSmartActions);
+        out.writeBundle(mSmartReplies);
     }
 
     public static final Parcelable.Creator<NotificationRankingUpdate> CREATOR
@@ -167,4 +172,8 @@
     public Bundle getSmartActions() {
         return mSmartActions;
     }
+
+    public Bundle getSmartReplies() {
+        return mSmartReplies;
+    }
 }
diff --git a/core/java/android/view/ViewPropertyAnimator.java b/core/java/android/view/ViewPropertyAnimator.java
index 6c84b63..e3e2069 100644
--- a/core/java/android/view/ViewPropertyAnimator.java
+++ b/core/java/android/view/ViewPropertyAnimator.java
@@ -109,11 +109,6 @@
     private ValueAnimator mTempValueAnimator;
 
     /**
-     * A RenderThread-driven backend that may intercept startAnimation
-     */
-    private ViewPropertyAnimatorRT mRTBackend;
-
-    /**
      * This listener is the mechanism by which the underlying Animator causes changes to the
      * properties currently being animated, as well as the cleanup after an animation is
      * complete.
@@ -434,9 +429,6 @@
         mPendingOnStartAction = null;
         mPendingOnEndAction = null;
         mView.removeCallbacks(mAnimationStarter);
-        if (mRTBackend != null) {
-            mRTBackend.cancelAll();
-        }
     }
 
     /**
@@ -859,9 +851,6 @@
      * value accordingly.
      */
     private void startAnimation() {
-        if (mRTBackend != null && mRTBackend.startAnimation(this)) {
-            return;
-        }
         mView.setHasTransientState(true);
         ValueAnimator animator = ValueAnimator.ofFloat(1.0f);
         ArrayList<NameValuesHolder> nameValueList =
diff --git a/core/java/android/view/ViewPropertyAnimatorRT.java b/core/java/android/view/ViewPropertyAnimatorRT.java
deleted file mode 100644
index de96887..0000000
--- a/core/java/android/view/ViewPropertyAnimatorRT.java
+++ /dev/null
@@ -1,138 +0,0 @@
-/*
- * Copyright (C) 2014 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 android.view;
-
-import android.animation.TimeInterpolator;
-import android.view.ViewPropertyAnimator.NameValuesHolder;
-import android.view.animation.Interpolator;
-import android.view.animation.LinearInterpolator;
-
-import com.android.internal.view.animation.FallbackLUTInterpolator;
-
-import java.util.ArrayList;
-
-
-/**
- * This is a RenderThread driven backend for ViewPropertyAnimator.
- */
-class ViewPropertyAnimatorRT {
-
-    private static final Interpolator sLinearInterpolator = new LinearInterpolator();
-
-    private final View mView;
-
-    private RenderNodeAnimator mAnimators[] = new RenderNodeAnimator[RenderNodeAnimator.LAST_VALUE + 1];
-
-    ViewPropertyAnimatorRT(View view) {
-        mView = view;
-    }
-
-    /**
-     * @return true if ViewPropertyAnimatorRT handled the animation,
-     *         false if ViewPropertyAnimator needs to handle it
-     */
-    public boolean startAnimation(ViewPropertyAnimator parent) {
-        cancelAnimators(parent.mPendingAnimations);
-        if (!canHandleAnimator(parent)) {
-            return false;
-        }
-        doStartAnimation(parent);
-        return true;
-    }
-
-    public void cancelAll() {
-        for (int i = 0; i < mAnimators.length; i++) {
-            if (mAnimators[i] != null) {
-                mAnimators[i].cancel();
-                mAnimators[i] = null;
-            }
-        }
-    }
-
-    private void doStartAnimation(ViewPropertyAnimator parent) {
-        int size = parent.mPendingAnimations.size();
-
-        long startDelay = parent.getStartDelay();
-        long duration = parent.getDuration();
-        TimeInterpolator interpolator = parent.getInterpolator();
-        if (interpolator == null) {
-            // Documented to be LinearInterpolator in ValueAnimator.setInterpolator
-            interpolator = sLinearInterpolator;
-        }
-        if (!RenderNodeAnimator.isNativeInterpolator(interpolator)) {
-            interpolator = new FallbackLUTInterpolator(interpolator, duration);
-        }
-        for (int i = 0; i < size; i++) {
-            NameValuesHolder holder = parent.mPendingAnimations.get(i);
-            int property = RenderNodeAnimator.mapViewPropertyToRenderProperty(holder.mNameConstant);
-
-            final float finalValue = holder.mFromValue + holder.mDeltaValue;
-            RenderNodeAnimator animator = new RenderNodeAnimator(property, finalValue);
-            animator.setStartDelay(startDelay);
-            animator.setDuration(duration);
-            animator.setInterpolator(interpolator);
-            animator.setTarget(mView);
-            animator.start();
-
-            mAnimators[property] = animator;
-        }
-
-        parent.mPendingAnimations.clear();
-    }
-
-    private boolean canHandleAnimator(ViewPropertyAnimator parent) {
-        // TODO: Can we eliminate this entirely?
-        // If RenderNode.animatorProperties() can be toggled to point at staging
-        // instead then RNA can be used as the animators for software as well
-        // as the updateListener fallback paths. If this can be toggled
-        // at the top level somehow, combined with requiresUiRedraw, we could
-        // ensure that RT does not self-animate, allowing for safe driving of
-        // the animators from the UI thread using the same mechanisms
-        // ViewPropertyAnimator does, just with everything sitting on a single
-        // animator subsystem instead of multiple.
-
-        if (parent.getUpdateListener() != null) {
-            return false;
-        }
-        if (parent.getListener() != null) {
-            // TODO support
-            return false;
-        }
-        if (!mView.isHardwareAccelerated()) {
-            // TODO handle this maybe?
-            return false;
-        }
-        if (parent.hasActions()) {
-            return false;
-        }
-        // Here goes nothing...
-        return true;
-    }
-
-    private void cancelAnimators(ArrayList<NameValuesHolder> mPendingAnimations) {
-        int size = mPendingAnimations.size();
-        for (int i = 0; i < size; i++) {
-            NameValuesHolder holder = mPendingAnimations.get(i);
-            int property = RenderNodeAnimator.mapViewPropertyToRenderProperty(holder.mNameConstant);
-            if (mAnimators[property] != null) {
-                mAnimators[property].cancel();
-                mAnimators[property] = null;
-            }
-        }
-    }
-
-}
diff --git a/core/java/android/view/animation/Animation.java b/core/java/android/view/animation/Animation.java
index 64686dd..87b7b05 100644
--- a/core/java/android/view/animation/Animation.java
+++ b/core/java/android/view/animation/Animation.java
@@ -19,6 +19,7 @@
 import android.annotation.AnimRes;
 import android.annotation.ColorInt;
 import android.annotation.InterpolatorRes;
+import android.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.graphics.RectF;
@@ -183,6 +184,7 @@
     /**
      * The animation listener to be notified when the animation starts, ends or repeats.
      */
+    @UnsupportedAppUsage
     AnimationListener mListener;
 
     /**
@@ -211,9 +213,13 @@
     private boolean mMore = true;
     private boolean mOneMoreTime = true;
 
+    @UnsupportedAppUsage
     RectF mPreviousRegion = new RectF();
+    @UnsupportedAppUsage
     RectF mRegion = new RectF();
+    @UnsupportedAppUsage
     Transformation mTransformation = new Transformation();
+    @UnsupportedAppUsage
     Transformation mPreviousTransformation = new Transformation();
 
     private final CloseGuard guard = CloseGuard.get();
@@ -322,6 +328,7 @@
     /**
      * @hide
      */
+    @UnsupportedAppUsage
     public void detach() {
         if (mStarted && !mEnded) {
             mEnded = true;
@@ -1046,6 +1053,7 @@
      *
      * @hide
      */
+    @UnsupportedAppUsage
     public void getInvalidateRegion(int left, int top, int right, int bottom,
             RectF invalidate, Transformation transformation) {
 
@@ -1077,6 +1085,7 @@
      *
      * @hide
      */
+    @UnsupportedAppUsage
     public void initializeInvalidateRegion(int left, int top, int right, int bottom) {
         final RectF region = mPreviousRegion;
         region.set(left, top, right, bottom);
diff --git a/core/java/android/view/animation/AnimationUtils.java b/core/java/android/view/animation/AnimationUtils.java
index 29f8442..c877b9c 100644
--- a/core/java/android/view/animation/AnimationUtils.java
+++ b/core/java/android/view/animation/AnimationUtils.java
@@ -19,6 +19,7 @@
 import android.annotation.AnimRes;
 import android.annotation.InterpolatorRes;
 import android.annotation.TestApi;
+import android.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.res.Resources;
 import android.content.res.Resources.NotFoundException;
@@ -157,6 +158,7 @@
         return createAnimationFromXml(c, parser, null, Xml.asAttributeSet(parser));
     }
 
+    @UnsupportedAppUsage
     private static Animation createAnimationFromXml(Context c, XmlPullParser parser,
             AnimationSet parent, AttributeSet attrs) throws XmlPullParserException, IOException {
 
diff --git a/core/java/android/view/animation/Transformation.java b/core/java/android/view/animation/Transformation.java
index 8eb5b5c..58da04d 100644
--- a/core/java/android/view/animation/Transformation.java
+++ b/core/java/android/view/animation/Transformation.java
@@ -17,6 +17,7 @@
 package android.view.animation;
 
 import android.annotation.FloatRange;
+import android.annotation.UnsupportedAppUsage;
 import android.graphics.Matrix;
 import android.graphics.Rect;
 
@@ -238,6 +239,7 @@
      * Print short string, to optimize dumping.
      * @hide
      */
+    @UnsupportedAppUsage
     public void printShortString(PrintWriter pw) {
         pw.print("{alpha="); pw.print(mAlpha);
         pw.print(" matrix=");
diff --git a/core/java/android/view/animation/TranslateAnimation.java b/core/java/android/view/animation/TranslateAnimation.java
index 216022b..6c040d4 100644
--- a/core/java/android/view/animation/TranslateAnimation.java
+++ b/core/java/android/view/animation/TranslateAnimation.java
@@ -16,6 +16,7 @@
 
 package android.view.animation;
 
+import android.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.util.AttributeSet;
@@ -34,13 +35,17 @@
     private int mToYType = ABSOLUTE;
 
     /** @hide */
+    @UnsupportedAppUsage
     protected float mFromXValue = 0.0f;
     /** @hide */
+    @UnsupportedAppUsage
     protected float mToXValue = 0.0f;
 
     /** @hide */
+    @UnsupportedAppUsage
     protected float mFromYValue = 0.0f;
     /** @hide */
+    @UnsupportedAppUsage
     protected float mToYValue = 0.0f;
 
     /** @hide */
diff --git a/core/java/android/view/animation/TranslateYAnimation.java b/core/java/android/view/animation/TranslateYAnimation.java
index 714558d..a6e0ccb 100644
--- a/core/java/android/view/animation/TranslateYAnimation.java
+++ b/core/java/android/view/animation/TranslateYAnimation.java
@@ -16,6 +16,7 @@
 
 package android.view.animation;
 
+import android.annotation.UnsupportedAppUsage;
 import android.graphics.Matrix;
 
 /**
@@ -38,6 +39,7 @@
     /**
      * Constructor. Passes in 0 for the x parameters of TranslateAnimation
      */
+    @UnsupportedAppUsage
     public TranslateYAnimation(int fromYType, float fromYValue, int toYType, float toYValue) {
         super(ABSOLUTE, 0, ABSOLUTE, 0, fromYType, fromYValue, toYType, toYValue);
     }
diff --git a/core/java/com/android/internal/os/BinderCallsStats.java b/core/java/com/android/internal/os/BinderCallsStats.java
index baf6a4d..eb369e1 100644
--- a/core/java/com/android/internal/os/BinderCallsStats.java
+++ b/core/java/com/android/internal/os/BinderCallsStats.java
@@ -17,12 +17,19 @@
 package com.android.internal.os;
 
 import android.annotation.Nullable;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.BatteryManager;
+import android.os.BatteryManagerInternal;
 import android.os.Binder;
+import android.os.OsProtoEnums;
+import android.os.PowerManager;
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.text.format.DateFormat;
 import android.util.ArrayMap;
-import android.util.Log;
 import android.util.Pair;
 import android.util.Slog;
 import android.util.SparseArray;
@@ -31,6 +38,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.os.BinderInternal.CallSession;
 import com.android.internal.util.Preconditions;
+import com.android.server.LocalServices;
 
 import java.io.PrintWriter;
 import java.lang.reflect.InvocationTargetException;
@@ -74,12 +82,99 @@
     private final Random mRandom;
     private long mStartTime = System.currentTimeMillis();
 
-    public BinderCallsStats(Random random) {
-        this.mRandom = random;
+    // State updated by the broadcast receiver below.
+    private boolean mScreenInteractive;
+    private boolean mCharging;
+    private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            switch (intent.getAction()) {
+                case Intent.ACTION_BATTERY_CHANGED:
+                    mCharging = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0) != 0;
+                    break;
+                case Intent.ACTION_SCREEN_ON:
+                    mScreenInteractive = true;
+                    break;
+                case Intent.ACTION_SCREEN_OFF:
+                    mScreenInteractive = false;
+                    break;
+            }
+        }
+    };
+
+    /** Injector for {@link BinderCallsStats}. */
+    public static class Injector {
+        public Random getRandomGenerator() {
+            return new Random();
+        }
+    }
+
+    public BinderCallsStats(Injector injector) {
+        this.mRandom = injector.getRandomGenerator();
+    }
+
+    public void systemReady(Context context) {
+        registerBroadcastReceiver(context);
+        setInitialState(queryScreenInteractive(context), queryIsCharging());
+    }
+
+    /**
+     * Listens for screen/battery state changes.
+     */
+    @VisibleForTesting
+    public void registerBroadcastReceiver(Context context) {
+        final IntentFilter filter = new IntentFilter();
+        filter.addAction(Intent.ACTION_BATTERY_CHANGED);
+        filter.addAction(Intent.ACTION_SCREEN_ON);
+        filter.addAction(Intent.ACTION_SCREEN_OFF);
+        filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
+        context.registerReceiver(mBroadcastReceiver, filter);
+    }
+
+    /**
+     * Sets the battery/screen initial state.
+     *
+     * This has to be updated *after* the broadcast receiver is installed.
+     */
+    @VisibleForTesting
+    public void setInitialState(boolean isScreenInteractive, boolean isCharging) {
+        this.mScreenInteractive = isScreenInteractive;
+        this.mCharging = isCharging;
+        // Data collected previously was not accurate since the battery/screen state was not set.
+        reset();
+    }
+
+    private boolean queryIsCharging() {
+        final BatteryManagerInternal batteryManager =
+                LocalServices.getService(BatteryManagerInternal.class);
+        if (batteryManager == null) {
+            Slog.wtf(TAG, "BatteryManager null while starting BinderCallsStatsService");
+            // Default to true to not collect any data.
+            return true;
+        } else {
+            return batteryManager.getPlugType() != OsProtoEnums.BATTERY_PLUGGED_NONE;
+        }
+    }
+
+    private boolean queryScreenInteractive(Context context) {
+        final PowerManager powerManager = context.getSystemService(PowerManager.class);
+        final boolean screenInteractive;
+        if (powerManager == null) {
+            Slog.wtf(TAG, "PowerManager null while starting BinderCallsStatsService",
+                    new Throwable());
+            return true;
+        } else {
+            return powerManager.isInteractive();
+        }
     }
 
     @Override
+    @Nullable
     public CallSession callStarted(Binder binder, int code) {
+        if (mCharging) {
+            return null;
+        }
+
         final CallSession s = obtainCallSession();
         s.binderClass = binder.getClass();
         s.transactionCode = code;
@@ -126,9 +221,15 @@
         final int callingUid = getCallingUid();
 
         synchronized (mLock) {
+            // This was already checked in #callStart but check again while synchronized.
+            if (mCharging) {
+                return;
+            }
+
             final UidEntry uidEntry = getUidEntry(callingUid);
             uidEntry.callCount++;
-            final CallStat callStat = uidEntry.getOrCreate(s.binderClass, s.transactionCode);
+            final CallStat callStat = uidEntry.getOrCreate(
+                    s.binderClass, s.transactionCode, mScreenInteractive);
             callStat.callCount++;
 
             if (recordCall) {
@@ -178,7 +279,7 @@
             }
         } catch (RuntimeException e) {
             // Do not propagate the exception. We do not want to swallow original exception.
-            Log.wtf(TAG, "Unexpected exception while updating mExceptionCounts", e);
+            Slog.wtf(TAG, "Unexpected exception while updating mExceptionCounts");
         }
     }
 
@@ -225,6 +326,7 @@
                     exported.className = stat.binderClass.getName();
                     exported.binderClass = stat.binderClass;
                     exported.transactionCode = stat.transactionCode;
+                    exported.screenInteractive = stat.screenInteractive;
                     exported.cpuTimeMicros = stat.cpuTimeMicros;
                     exported.maxCpuTimeMicros = stat.maxCpuTimeMicros;
                     exported.latencyMicros = stat.latencyMicros;
@@ -308,7 +410,8 @@
         final List<UidEntry> topEntries = verbose ? entries
                 : getHighestValues(entries, value -> value.cpuTimeMicros, 0.9);
         pw.println("Per-UID raw data " + datasetSizeDesc
-                + "(package/uid, call_desc, cpu_time_micros, max_cpu_time_micros, "
+                + "(package/uid, call_desc, screen_interactive, "
+                + "cpu_time_micros, max_cpu_time_micros, "
                 + "latency_time_micros, max_latency_time_micros, exception_count, "
                 + "max_request_size_bytes, max_reply_size_bytes, recorded_call_count, "
                 + "call_count):");
@@ -320,6 +423,7 @@
                     .append(uidToString(e.uid, appIdToPkgNameMap))
                     .append(',').append(e.className)
                     .append('#').append(e.methodName)
+                    .append(',').append(e.screenInteractive)
                     .append(',').append(e.cpuTimeMicros)
                     .append(',').append(e.maxCpuTimeMicros)
                     .append(',').append(e.latencyMicros)
@@ -441,6 +545,8 @@
     public static class CallStat {
         public Class<? extends Binder> binderClass;
         public int transactionCode;
+        // True if the screen was interactive when the call ended.
+        public boolean screenInteractive;
         // Number of calls for which we collected data for. We do not record data for all the calls
         // when sampling is on.
         public long recordedCallCount;
@@ -461,9 +567,11 @@
         public long maxReplySizeBytes;
         public long exceptionCount;
 
-        CallStat(Class<? extends Binder> binderClass, int transactionCode) {
+        CallStat(Class<? extends Binder> binderClass, int transactionCode,
+                boolean screenInteractive) {
             this.binderClass = binderClass;
             this.transactionCode = transactionCode;
+            this.screenInteractive = screenInteractive;
         }
     }
 
@@ -471,6 +579,7 @@
     public static class CallStatKey {
         public Class<? extends Binder> binderClass;
         public int transactionCode;
+        private boolean screenInteractive;
 
         @Override
         public boolean equals(Object o) {
@@ -480,6 +589,7 @@
 
             final CallStatKey key = (CallStatKey) o;
             return transactionCode == key.transactionCode
+                    && screenInteractive == key.screenInteractive
                     && (binderClass.equals(key.binderClass));
         }
 
@@ -487,6 +597,7 @@
         public int hashCode() {
             int result = binderClass.hashCode();
             result = 31 * result + transactionCode;
+            result = 31 * result + (screenInteractive ? 1231 : 1237);
             return result;
         }
     }
@@ -513,17 +624,20 @@
         private Map<CallStatKey, CallStat> mCallStats = new ArrayMap<>();
         private CallStatKey mTempKey = new CallStatKey();
 
-        CallStat getOrCreate(Class<? extends Binder> binderClass, int transactionCode) {
+        CallStat getOrCreate(Class<? extends Binder> binderClass, int transactionCode,
+                boolean screenInteractive) {
             // Use a global temporary key to avoid creating new objects for every lookup.
             mTempKey.binderClass = binderClass;
             mTempKey.transactionCode = transactionCode;
+            mTempKey.screenInteractive = screenInteractive;
             CallStat mapCallStat = mCallStats.get(mTempKey);
             // Only create CallStat if it's a new entry, otherwise update existing instance
             if (mapCallStat == null) {
-                mapCallStat = new CallStat(binderClass, transactionCode);
+                mapCallStat = new CallStat(binderClass, transactionCode, screenInteractive);
                 CallStatKey key = new CallStatKey();
                 key.binderClass = binderClass;
                 key.transactionCode = transactionCode;
+                key.screenInteractive = screenInteractive;
                 mCallStats.put(key, mapCallStat);
             }
             return mapCallStat;
@@ -592,6 +706,11 @@
         return result;
     }
 
+    @VisibleForTesting
+    public BroadcastReceiver getBroadcastReceiver() {
+        return mBroadcastReceiver;
+    }
+
     private static int compareByCpuDesc(
             ExportedCallStat a, ExportedCallStat b) {
         return Long.compare(b.cpuTimeMicros, a.cpuTimeMicros);
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index 2790324..616520f 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -140,14 +140,14 @@
 
     void showShutdownUi(boolean isReboot, String reason);
 
-    // Used to show the dialog when FingerprintService starts authentication
-    void showFingerprintDialog(in Bundle bundle, IBiometricPromptReceiver receiver);
-    // Used to hide the dialog when a finger is authenticated
-    void onFingerprintAuthenticated();
+    // Used to show the dialog when BiometricService starts authentication
+    void showBiometricDialog(in Bundle bundle, IBiometricPromptReceiver receiver);
+    // Used to hide the dialog when a biometric is authenticated
+    void onBiometricAuthenticated();
     // Used to set a temporary message, e.g. fingerprint not recognized, finger moved too fast, etc
-    void onFingerprintHelp(String message);
+    void onBiometricHelp(String message);
     // Used to set a message - the dialog will dismiss after a certain amount of time
-    void onFingerprintError(String error);
-    // Used to hide the fingerprint dialog when the authenticationclient is stopped
-    void hideFingerprintDialog();
+    void onBiometricError(String error);
+    // Used to hide the biometric dialog when the AuthenticationClient is stopped
+    void hideBiometricDialog();
 }
diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
index 159d49b..a79e15a 100644
--- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
@@ -90,14 +90,14 @@
     void showPinningEnterExitToast(boolean entering);
     void showPinningEscapeToast();
 
-    // Used to show the dialog when FingerprintService starts authentication
-    void showFingerprintDialog(in Bundle bundle, IBiometricPromptReceiver receiver);
-    // Used to hide the dialog when a finger is authenticated
-    void onFingerprintAuthenticated();
+    // Used to show the dialog when BiometricService starts authentication
+    void showBiometricDialog(in Bundle bundle, IBiometricPromptReceiver receiver);
+    // Used to hide the dialog when a biometric is authenticated
+    void onBiometricAuthenticated();
     // Used to set a temporary message, e.g. fingerprint not recognized, finger moved too fast, etc
-    void onFingerprintHelp(String message);
+    void onBiometricHelp(String message);
     // Used to set a message - the dialog will dismiss after a certain amount of time
-    void onFingerprintError(String error);
-    // Used to hide the fingerprint dialog when the authenticationclient is stopped
-    void hideFingerprintDialog();
+    void onBiometricError(String error);
+    // Used to hide the biometric dialog when the AuthenticationClient is stopped
+    void hideBiometricDialog();
 }
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 6856e29..6316da5 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -155,6 +155,7 @@
         "android/graphics/Utils.cpp",
         "android/graphics/YuvToJpegEncoder.cpp",
         "android/graphics/fonts/Font.cpp",
+        "android/graphics/fonts/FontFamily.cpp",
         "android/graphics/pdf/PdfDocument.cpp",
         "android/graphics/pdf/PdfEditor.cpp",
         "android/graphics/pdf/PdfRenderer.cpp",
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 1820888..7fe095b 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -141,6 +141,7 @@
 extern int register_android_graphics_drawable_AnimatedVectorDrawable(JNIEnv* env);
 extern int register_android_graphics_drawable_VectorDrawable(JNIEnv* env);
 extern int register_android_graphics_fonts_Font(JNIEnv* env);
+extern int register_android_graphics_fonts_FontFamily(JNIEnv* env);
 extern int register_android_graphics_pdf_PdfDocument(JNIEnv* env);
 extern int register_android_graphics_pdf_PdfEditor(JNIEnv* env);
 extern int register_android_graphics_pdf_PdfRenderer(JNIEnv* env);
@@ -1408,6 +1409,7 @@
     REG_JNI(register_android_graphics_drawable_AnimatedVectorDrawable),
     REG_JNI(register_android_graphics_drawable_VectorDrawable),
     REG_JNI(register_android_graphics_fonts_Font),
+    REG_JNI(register_android_graphics_fonts_FontFamily),
     REG_JNI(register_android_graphics_pdf_PdfDocument),
     REG_JNI(register_android_graphics_pdf_PdfEditor),
     REG_JNI(register_android_graphics_pdf_PdfRenderer),
diff --git a/core/jni/android/graphics/Camera.cpp b/core/jni/android/graphics/Camera.cpp
index 76d6851..da95497 100644
--- a/core/jni/android/graphics/Camera.cpp
+++ b/core/jni/android/graphics/Camera.cpp
@@ -96,10 +96,12 @@
 }
 
 static void Camera_applyToCanvas(JNIEnv* env, jobject obj, jlong canvasHandle) {
-    SkCanvas* canvas = reinterpret_cast<android::Canvas*>(canvasHandle)->asSkCanvas();
+    android::Canvas* canvas = reinterpret_cast<android::Canvas*>(canvasHandle);
     jlong viewHandle = env->GetLongField(obj, gNativeInstanceFieldID);
     Sk3DView* v = reinterpret_cast<Sk3DView*>(viewHandle);
-    v->applyToCanvas(canvas);
+    SkMatrix matrix;
+    v->getMatrix(&matrix);
+    canvas->concat(matrix);
 }
 
 static jfloat Camera_dotWithNormal(JNIEnv* env, jobject obj,
diff --git a/core/jni/android/graphics/fonts/FontFamily.cpp b/core/jni/android/graphics/fonts/FontFamily.cpp
new file mode 100644
index 0000000..4597386
--- /dev/null
+++ b/core/jni/android/graphics/fonts/FontFamily.cpp
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#define LOG_TAG "Minikin"
+
+#include <nativehelper/JNIHelp.h>
+#include <core_jni_helpers.h>
+
+#include "FontUtils.h"
+
+#include <minikin/FontFamily.h>
+
+#include <memory>
+
+namespace android {
+
+struct NativeFamilyBuilder {
+    std::vector<minikin::Font> fonts;
+};
+
+static inline NativeFamilyBuilder* toBuilder(jlong ptr) {
+    return reinterpret_cast<NativeFamilyBuilder*>(ptr);
+}
+
+static inline FontWrapper* toFontWrapper(jlong ptr) {
+    return reinterpret_cast<FontWrapper*>(ptr);
+}
+
+static void releaseFontFamily(jlong family) {
+    delete reinterpret_cast<FontFamilyWrapper*>(family);
+}
+
+// Regular JNI
+static jlong FontFamily_Builder_initBuilder(JNIEnv*, jobject) {
+    return reinterpret_cast<jlong>(new NativeFamilyBuilder());
+}
+
+// Critical Native
+static void FontFamily_Builder_addFont(jlong builderPtr, jlong fontPtr) {
+    toBuilder(builderPtr)->fonts.push_back(toFontWrapper(fontPtr)->font);
+}
+
+// Regular JNI
+static jlong FontFamily_Builder_build(JNIEnv* env, jobject clazz, jlong builderPtr) {
+    std::unique_ptr<NativeFamilyBuilder> builder(toBuilder(builderPtr));
+    std::shared_ptr<minikin::FontFamily> family =
+            std::make_shared<minikin::FontFamily>(std::move(builder->fonts));
+    if (family->getCoverage().length() == 0) {
+        // No coverage means minikin rejected given font for some reasons.
+        jniThrowException(env, "java/lang/IllegalArgumentException",
+                          "Failed to create internal object. maybe invalid font data");
+        return 0;
+    }
+    return reinterpret_cast<jlong>(new FontFamilyWrapper(std::move(family)));
+}
+
+// CriticalNative
+static jlong FontFamily_Builder_GetReleaseFunc() {
+    return reinterpret_cast<jlong>(releaseFontFamily);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static const JNINativeMethod gFontFamilyBuilderMethods[] = {
+    { "nInitBuilder", "()J", (void*) FontFamily_Builder_initBuilder },
+    { "nAddFont", "(JJ)V", (void*) FontFamily_Builder_addFont },
+    { "nBuild", "(J)J", (void*) FontFamily_Builder_build },
+
+    { "nGetReleaseNativeFamily", "()J", (void*) FontFamily_Builder_GetReleaseFunc },
+};
+
+int register_android_graphics_fonts_FontFamily(JNIEnv* env) {
+    return RegisterMethodsOrDie(env, "android/graphics/fonts/FontFamily$Builder",
+            gFontFamilyBuilderMethods, NELEM(gFontFamilyBuilderMethods));
+}
+
+}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index ea99afe..f9d81ba 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -3256,6 +3256,13 @@
     <permission android:name="android.permission.MODIFY_AUDIO_ROUTING"
         android:protectionLevel="signature|privileged" />
 
+    <!-- Allows an application to modify what effects are applied to all audio
+         (matching certain criteria) from any application.
+         <p>Not for use by third-party applications.</p>
+         @hide -->
+    <permission android:name="android.permission.MODIFY_DEFAULT_AUDIO_EFFECTS"
+        android:protectionLevel="signature|privileged" />
+
     <!-- @SystemApi Allows an application to capture video output.
          <p>Not for use by third-party applications.</p> -->
     <permission android:name="android.permission.CAPTURE_VIDEO_OUTPUT"
@@ -3508,6 +3515,10 @@
     <permission android:name="android.permission.BIND_SETTINGS_SUGGESTIONS_SERVICE"
                 android:protectionLevel="signature" />
 
+    <!-- @hide Internal permission to allows an application to access card content provider. -->
+    <permission android:name="android.permission.WRITE_SETTINGS_HOMEPAGE_DATA"
+                android:protectionLevel="signature|privileged" />
+
     <!-- @SystemApi Allows applications to set a live wallpaper.
          @hide XXX Change to signature once the picker is moved to its
          own apk as Ghod Intended. -->
diff --git a/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java b/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java
index 5599ee6..2f83190 100644
--- a/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java
@@ -16,7 +16,11 @@
 
 package com.android.internal.os;
 
+import android.content.Context;
+import android.content.Intent;
+import android.os.BatteryManager;
 import android.os.Binder;
+import android.os.OsProtoEnums;
 import android.platform.test.annotations.Presubmit;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
@@ -392,6 +396,113 @@
     }
 
     @Test
+    public void testDataResetWhenInitialStateSet() {
+        TestBinderCallsStats bcs = new TestBinderCallsStats();
+        bcs.setDetailedTracking(true);
+        Binder binder = new Binder();
+        CallSession callSession = bcs.callStarted(binder, 1);
+        bcs.time += 10;
+        bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+
+        bcs.setInitialState(true, true);
+
+        SparseArray<BinderCallsStats.UidEntry> uidEntries = bcs.getUidEntries();
+        assertEquals(0, uidEntries.size());
+    }
+
+    @Test
+    public void testScreenAndChargerInitialStates() {
+        TestBinderCallsStats bcs = new TestBinderCallsStats();
+        bcs.setDetailedTracking(true);
+        Binder binder = new Binder();
+        bcs.setInitialState(true /** screen iteractive */, false);
+
+        CallSession callSession = bcs.callStarted(binder, 1);
+        bcs.time += 10;
+        bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+
+        List<BinderCallsStats.CallStat> callStatsList =
+                new ArrayList(bcs.getUidEntries().get(TEST_UID).getCallStatsList());
+        assertEquals(true, callStatsList.get(0).screenInteractive);
+    }
+
+    @Test
+    public void testNoDataCollectedOnCharger() {
+        TestBinderCallsStats bcs = new TestBinderCallsStats();
+        bcs.setDetailedTracking(true);
+        Intent intent = new Intent(Intent.ACTION_BATTERY_CHANGED)
+                .putExtra(BatteryManager.EXTRA_PLUGGED, OsProtoEnums.BATTERY_PLUGGED_AC);
+        bcs.getBroadcastReceiver().onReceive(null, intent);
+        Binder binder = new Binder();
+        CallSession callSession = bcs.callStarted(binder, 1);
+        bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+
+        assertEquals(0, bcs.getUidEntries().size());
+    }
+
+    @Test
+    public void testScreenOff() {
+        TestBinderCallsStats bcs = new TestBinderCallsStats();
+        bcs.setDetailedTracking(true);
+        bcs.getBroadcastReceiver().onReceive(null, new Intent(Intent.ACTION_SCREEN_OFF));
+        Binder binder = new Binder();
+        CallSession callSession = bcs.callStarted(binder, 1);
+        bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+
+        SparseArray<BinderCallsStats.UidEntry> uidEntries = bcs.getUidEntries();
+        assertEquals(1, uidEntries.size());
+        BinderCallsStats.UidEntry uidEntry = uidEntries.get(TEST_UID);
+        Assert.assertNotNull(uidEntry);
+        List<BinderCallsStats.CallStat> callStatsList = new ArrayList(uidEntry.getCallStatsList());
+        assertEquals(false, callStatsList.get(0).screenInteractive);
+    }
+
+    @Test
+    public void testScreenOn() {
+        TestBinderCallsStats bcs = new TestBinderCallsStats();
+        bcs.setDetailedTracking(true);
+        bcs.getBroadcastReceiver().onReceive(null, new Intent(Intent.ACTION_SCREEN_ON));
+        Binder binder = new Binder();
+        CallSession callSession = bcs.callStarted(binder, 1);
+        bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+
+        SparseArray<BinderCallsStats.UidEntry> uidEntries = bcs.getUidEntries();
+        assertEquals(1, uidEntries.size());
+        BinderCallsStats.UidEntry uidEntry = uidEntries.get(TEST_UID);
+        Assert.assertNotNull(uidEntry);
+        List<BinderCallsStats.CallStat> callStatsList = new ArrayList(uidEntry.getCallStatsList());
+        assertEquals(true, callStatsList.get(0).screenInteractive);
+    }
+
+    @Test
+    public void testOnCharger() {
+        TestBinderCallsStats bcs = new TestBinderCallsStats();
+        bcs.setDetailedTracking(true);
+        Intent intent = new Intent(Intent.ACTION_BATTERY_CHANGED)
+                .putExtra(BatteryManager.EXTRA_PLUGGED, OsProtoEnums.BATTERY_PLUGGED_AC);
+        bcs.getBroadcastReceiver().onReceive(null, intent);
+        Binder binder = new Binder();
+        CallSession callSession = bcs.callStarted(binder, 1);
+        bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+
+        assertEquals(0, bcs.getExportedCallStats().size());
+    }
+
+    @Test
+    public void testOnBattery() {
+        TestBinderCallsStats bcs = new TestBinderCallsStats();
+        bcs.setDetailedTracking(true);
+        Intent intent = new Intent(Intent.ACTION_BATTERY_CHANGED)
+                .putExtra(BatteryManager.EXTRA_PLUGGED, OsProtoEnums.BATTERY_PLUGGED_NONE);
+        bcs.getBroadcastReceiver().onReceive(null, intent);
+        Binder binder = new Binder();
+        CallSession callSession = bcs.callStarted(binder, 1);
+        bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
+
+        assertEquals(1, bcs.getExportedCallStats().size());
+    }
+
+    @Test
     public void testDumpDoesNotThrowException() {
         TestBinderCallsStats bcs = new TestBinderCallsStats();
         bcs.setDetailedTracking(true);
@@ -419,6 +530,8 @@
     public void testGetExportedStatsWhenDetailedTrackingEnabled() {
         TestBinderCallsStats bcs = new TestBinderCallsStats();
         bcs.setDetailedTracking(true);
+        bcs.getBroadcastReceiver().onReceive(null, new Intent(Intent.ACTION_SCREEN_ON));
+
         Binder binder = new Binder();
         CallSession callSession = bcs.callStarted(binder, 1);
         bcs.time += 10;
@@ -430,6 +543,7 @@
         assertEquals(TEST_UID, stat.uid);
         assertEquals("android.os.Binder", stat.className);
         assertEquals("1", stat.methodName);
+        assertEquals(true, stat.screenInteractive);
         assertEquals(10, stat.cpuTimeMicros);
         assertEquals(10, stat.maxCpuTimeMicros);
         assertEquals(20, stat.latencyMicros);
@@ -462,11 +576,15 @@
 
         TestBinderCallsStats() {
             // Make random generator not random.
-            super(new Random() {
-                int mCallCount = 0;
+            super(new Injector() {
+                public Random getRandomGenerator() {
+                    return new Random() {
+                        int mCallCount = 0;
 
-                public int nextInt() {
-                    return mCallCount++;
+                        public int nextInt() {
+                            return mCallCount++;
+                        }
+                    };
                 }
             });
         }
diff --git a/graphics/java/android/graphics/Camera.java b/graphics/java/android/graphics/Camera.java
index 3388941..cbd4ead 100644
--- a/graphics/java/android/graphics/Camera.java
+++ b/graphics/java/android/graphics/Camera.java
@@ -24,8 +24,6 @@
  * {@link Canvas}.
  */
 public class Camera {
-    private Matrix mMatrix;
-
     /**
      * Creates a new camera, with empty transformations.
      */
@@ -151,13 +149,7 @@
      * @param canvas The Canvas to set the transform matrix onto
      */
     public void applyToCanvas(Canvas canvas) {
-        if (canvas.isHardwareAccelerated()) {
-            if (mMatrix == null) mMatrix = new Matrix();
-            getMatrix(mMatrix);
-            canvas.concat(mMatrix);
-        } else {
-            nativeApplyToCanvas(canvas.getNativeCanvasWrapper());
-        }
+        nativeApplyToCanvas(canvas.getNativeCanvasWrapper());
     }
 
     public native float dotWithNormal(float dx, float dy, float dz);
diff --git a/graphics/java/android/graphics/fonts/Font.java b/graphics/java/android/graphics/fonts/Font.java
index 9da61db9..9d94a64 100644
--- a/graphics/java/android/graphics/fonts/Font.java
+++ b/graphics/java/android/graphics/fonts/Font.java
@@ -469,4 +469,9 @@
     public long getNativePtr() {
         return mNativePtr;
     }
+
+    @Override
+    public String toString() {
+        return "Font {weight=" + mWeight + ", italic=" + mItalic + "}";
+    }
 }
diff --git a/graphics/java/android/graphics/fonts/FontFamily.java b/graphics/java/android/graphics/fonts/FontFamily.java
new file mode 100644
index 0000000..74b58ea
--- /dev/null
+++ b/graphics/java/android/graphics/fonts/FontFamily.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2018 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 android.graphics.fonts;
+
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+
+import com.android.internal.util.Preconditions;
+
+import dalvik.annotation.optimization.CriticalNative;
+
+import libcore.util.NativeAllocationRegistry;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+
+/**
+ * A font family class can be used for creating Typeface.
+ *
+ * <p>
+ * A font family is a bundle of fonts for drawing text in various styles.
+ * For example, you can bundle regular style font and bold style font into a single font family,
+ * then system will select the correct style font from family for drawing.
+ *
+ * <pre>
+ *  FontFamily family = new FontFamily.Builder(new Font.Builder("regular.ttf").build())
+ *      .addFont(new Font.Builder("bold.ttf").build()).build();
+ *  Typeface typeface = new Typeface.Builder2(family).build();
+ *
+ *  SpannableStringBuilder ssb = new SpannableStringBuilder("Hello, World.");
+ *  ssb.setSpan(new StyleSpan(Typeface.Bold), 6, 12, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ *
+ *  textView.setTypeface(typeface);
+ *  textView.setText(ssb);
+ * </pre>
+ *
+ * In this example, "Hello, " is drawn with "regular.ttf", and "World." is drawn with "bold.ttf".
+ *
+ * If there is no font exactly matches with the text style, the system will select the closest font.
+ * </p>
+ *
+ */
+public class FontFamily {
+    private static final String TAG = "FontFamily";
+
+    /**
+     * A builder class for creating new FontFamily.
+     */
+    public static class Builder {
+        private static final NativeAllocationRegistry sFamilyRegistory =
+                new NativeAllocationRegistry(FontFamily.class.getClassLoader(),
+                    nGetReleaseNativeFamily(), 64);
+
+        private final ArrayList<Font> mFonts = new ArrayList<>();
+        private final HashSet<Integer> mStyleHashSet = new HashSet<>();
+
+        /**
+         * Constructs a builder.
+         *
+         * @param font a font
+         */
+        public Builder(@NonNull Font font) {
+            Preconditions.checkNotNull(font, "font can not be null");
+            mStyleHashSet.add(makeStyleIdentifier(font));
+            mFonts.add(font);
+        }
+
+        /**
+         * Adds different style font to the builder.
+         *
+         * System will select the font if the text style is closest to the font.
+         * If the same style font is already added to the builder, this method will fail with
+         * {@link IllegalArgumentException}.
+         *
+         * Note that system assumes all fonts bundled in FontFamily have the same coverage for the
+         * code points. For example, regular style font and bold style font must have the same code
+         * point coverage, otherwise some character may be shown as tofu.
+         *
+         * @param font a font
+         * @return this builder
+         */
+        public @NonNull Builder addFont(@NonNull Font font) {
+            Preconditions.checkNotNull(font, "font can not be null");
+            if (!mStyleHashSet.add(makeStyleIdentifier(font))) {
+                throw new IllegalArgumentException(font + " has already been added");
+            }
+            mFonts.add(font);
+            return this;
+        }
+
+        /**
+         * Build the font family
+         * @return a font family
+         */
+        public @NonNull FontFamily build() {
+            final long builderPtr = nInitBuilder();
+            for (int i = 0; i < mFonts.size(); ++i) {
+                nAddFont(builderPtr, mFonts.get(i).getNativePtr());
+            }
+            final long ptr = nBuild(builderPtr);
+            final FontFamily family = new FontFamily(mFonts, ptr);
+            sFamilyRegistory.registerNativeAllocation(family, ptr);
+            return family;
+        }
+
+        private static int makeStyleIdentifier(@NonNull Font font) {
+            return font.getWeight() | (font.isItalic() ? (1 << 16) : 0);
+        }
+
+        private static native long nInitBuilder();
+        @CriticalNative
+        private static native void nAddFont(long builderPtr, long fontPtr);
+        private static native long nBuild(long builderPtr);
+        @CriticalNative
+        private static native long nGetReleaseNativeFamily();
+    }
+
+    private final ArrayList<Font> mFonts;
+    private final long mNativePtr;
+
+    // Use Builder instead.
+    private FontFamily(@NonNull ArrayList<Font> fonts, long ptr) {
+        mFonts = fonts;
+        mNativePtr = ptr;
+    }
+
+    /**
+     * Returns a font
+     *
+     * @param index an index of the font
+     * @return a registered font
+     */
+    public Font getFont(@IntRange(from = 0) int index) {
+        return mFonts.get(index);
+    }
+
+    /**
+     * Returns the number of fonts in this FontFamily.
+     *
+     * @return the number of fonts registered in this family.
+     */
+    public int getFontCount() {
+        return mFonts.size();
+    }
+
+    /** @hide */
+    public long getNativePtr() {
+        return mNativePtr;
+    }
+}
diff --git a/media/java/android/media/audiofx/DefaultEffect.java b/media/java/android/media/audiofx/DefaultEffect.java
new file mode 100644
index 0000000..a919868
--- /dev/null
+++ b/media/java/android/media/audiofx/DefaultEffect.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2018 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 android.media.audiofx;
+
+/**
+ * DefaultEffect is the base class for controlling default audio effects linked into the
+ * Android audio framework.
+ * <p>DefaultEffects are effects that get attached automatically to all AudioTracks,
+ * AudioRecords, and MediaPlayer instances meeting some criteria.
+ * <p>Applications should not use the DefaultEffect class directly but one of its derived classes
+ * to control specific types of defaults:
+ * <ul>
+ *   <li> {@link android.media.audiofx.StreamDefaultEffect}</li>
+ * </ul>
+ * <p>Creating a DefaultEffect object will register the corresponding effect engine as a default
+ * for the specified criteria. Whenever an audio session meets the criteria, an AudioEffect will
+ * be created and attached to it using the specified priority.
+ * @hide
+ */
+
+public abstract class DefaultEffect {
+    /**
+     * System wide unique default effect ID.
+     */
+    int mId;
+}
diff --git a/media/java/android/media/audiofx/StreamDefaultEffect.java b/media/java/android/media/audiofx/StreamDefaultEffect.java
new file mode 100644
index 0000000..9b1a21a
--- /dev/null
+++ b/media/java/android/media/audiofx/StreamDefaultEffect.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2018 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 android.media.audiofx;
+
+import android.annotation.RequiresPermission;
+import android.app.ActivityThread;
+import android.util.Log;
+import java.util.UUID;
+
+/**
+ * StreamDefaultEffect is a default effect that attaches automatically to all AudioTracks and
+ * MediaPlayer instances of a given stream type.
+ * <p>see {@link android.media.audiofx.DefaultEffect} class for more details on default effects.
+ * @hide
+ */
+
+public class StreamDefaultEffect extends DefaultEffect {
+    static {
+        System.loadLibrary("audioeffect_jni");
+    }
+
+    private final static String TAG = "StreamDefaultEffect-JAVA";
+
+    /**
+     * Class constructor.
+     *
+     * @param type type of effect engine to be default. This parameter is ignored if uuid is set,
+     *             and can be set to {@link android.media.audiofx.AudioEffect#EFFECT_TYPE_NULL}
+     *             in that case.
+     * @param uuid unique identifier of a particular effect implementation to be default. This
+     *             parameter can be set to
+     *             {@link android.media.audiofx.AudioEffect#EFFECT_TYPE_NULL}, in which case only
+     *             the type will be used to select the effect.
+     * @param priority the priority level requested by the application for controlling the effect
+     *             engine. As the same engine can be shared by several applications, this parameter
+     *             indicates how much the requesting application needs control of effect parameters.
+     *             The normal priority is 0, above normal is a positive number, below normal a
+     *             negative number.
+     * @param streamUsage a USAGE_* constant from {@link android.media.AudioAttributes} indicating
+     *             what streams the given effect should attach to by default. Note that similar
+     *             usages may share defaults.
+     *
+     * @throws java.lang.IllegalArgumentException
+     * @throws java.lang.UnsupportedOperationException
+     * @throws java.lang.RuntimeException
+     */
+    @RequiresPermission(value = android.Manifest.permission.MODIFY_DEFAULT_AUDIO_EFFECTS,
+                        conditional = true)  // Android Things uses an alternate permission.
+    public StreamDefaultEffect(UUID type, UUID uuid, int priority, int streamUsage) {
+        int[] id = new int[1];
+        int initResult = native_setup(type.toString(),
+                                      uuid.toString(),
+                                      priority,
+                                      streamUsage,
+                                      ActivityThread.currentOpPackageName(),
+                                      id);
+        if (initResult != AudioEffect.SUCCESS) {
+            Log.e(TAG, "Error code " + initResult + " when initializing StreamDefaultEffect");
+            switch (initResult) {
+                case AudioEffect.ERROR_BAD_VALUE:
+                    throw (new IllegalArgumentException(
+                            "Stream usage, type uuid, or implementation uuid not supported."));
+                case AudioEffect.ERROR_INVALID_OPERATION:
+                    throw (new UnsupportedOperationException(
+                            "Effect library not loaded"));
+                default:
+                    throw (new RuntimeException(
+                            "Cannot initialize effect engine for type: " + type
+                            + " Error: " + initResult));
+            }
+        }
+
+        mId = id[0];
+    }
+
+
+    /**
+     * Releases the native StreamDefaultEffect resources. It is a good practice to
+     * release the default effect when done with use as control can be returned to
+     * other applications or the native resources released.
+     */
+    public void release() {
+        native_release(mId);
+    }
+
+    @Override
+    protected void finalize() {
+        release();
+    }
+
+    // ---------------------------------------------------------
+    // Native methods called from the Java side
+    // --------------------
+
+    private native final int native_setup(String type,
+                                          String uuid,
+                                          int priority,
+                                          int streamUsage,
+                                          String opPackageName,
+                                          int[] id);
+
+    private native final void native_release(int id);
+}
diff --git a/media/jni/audioeffect/Android.bp b/media/jni/audioeffect/Android.bp
index 2aca0c1..0063c11 100644
--- a/media/jni/audioeffect/Android.bp
+++ b/media/jni/audioeffect/Android.bp
@@ -3,6 +3,7 @@
 
     srcs: [
         "android_media_AudioEffect.cpp",
+        "android_media_StreamDefaultEffect.cpp",
         "android_media_Visualizer.cpp",
     ],
 
diff --git a/media/jni/audioeffect/android_media_AudioEffect.cpp b/media/jni/audioeffect/android_media_AudioEffect.cpp
index 51b080a3..d3ba9f2 100644
--- a/media/jni/audioeffect/android_media_AudioEffect.cpp
+++ b/media/jni/audioeffect/android_media_AudioEffect.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include "android_media_AudioEffect.h"
+
 #include <stdio.h>
 
 //#define LOG_NDEBUG 0
@@ -71,7 +73,7 @@
 };
 
 
-static jint translateError(int code) {
+jint AudioEffectJni::translateNativeErrorToJava(int code) {
     switch(code) {
     case NO_ERROR:
         return AUDIOEFFECT_SUCCESS;
@@ -81,6 +83,10 @@
         return AUDIOEFFECT_ERROR_NO_INIT;
     case BAD_VALUE:
         return AUDIOEFFECT_ERROR_BAD_VALUE;
+    case NAME_NOT_FOUND:
+        // Name not found means the client tried to create an effect not found on the system,
+        // which is a form of bad value.
+        return AUDIOEFFECT_ERROR_BAD_VALUE;
     case INVALID_OPERATION:
         return AUDIOEFFECT_ERROR_INVALID_OPERATION;
     case NO_MEMORY:
@@ -359,7 +365,7 @@
         goto setup_failure;
     }
 
-    lStatus = translateError(lpAudioEffect->initCheck());
+    lStatus = AudioEffectJni::translateNativeErrorToJava(lpAudioEffect->initCheck());
     if (lStatus != AUDIOEFFECT_SUCCESS && lStatus != AUDIOEFFECT_ERROR_ALREADY_EXISTS) {
         ALOGE("AudioEffect initCheck failed %d", lStatus);
         goto setup_failure;
@@ -495,7 +501,7 @@
         return AUDIOEFFECT_ERROR_NO_INIT;
     }
 
-    return (jint) translateError(lpAudioEffect->setEnabled(enabled));
+    return AudioEffectJni::translateNativeErrorToJava(lpAudioEffect->setEnabled(enabled));
 }
 
 static jboolean
@@ -590,7 +596,7 @@
     if (lpValue != NULL) {
         env->ReleasePrimitiveArrayCritical(pJavaValue, lpValue, 0);
     }
-    return (jint) translateError(lStatus);
+    return AudioEffectJni::translateNativeErrorToJava(lStatus);
 }
 
 static jint
@@ -658,7 +664,7 @@
     if (lStatus == NO_ERROR) {
         return vsize;
     }
-    return (jint) translateError(lStatus);
+    return AudioEffectJni::translateNativeErrorToJava(lStatus);
 }
 
 static jint android_media_AudioEffect_native_command(JNIEnv *env, jobject thiz,
@@ -697,11 +703,12 @@
         }
     }
 
-    lStatus = translateError(lpAudioEffect->command((uint32_t)cmdCode,
-                                                    (uint32_t)cmdSize,
-                                                    pCmdData,
-                                                    (uint32_t *)&replySize,
-                                                    pReplyData));
+    lStatus = AudioEffectJni::translateNativeErrorToJava(
+            lpAudioEffect->command((uint32_t)cmdCode,
+                                   (uint32_t)cmdSize,
+                                   pCmdData,
+                                   (uint32_t *)&replySize,
+                                   pReplyData));
 
 command_Exit:
 
@@ -900,6 +907,7 @@
 
 // ----------------------------------------------------------------------------
 
+extern int register_android_media_StreamDefaultEffect(JNIEnv *env);
 extern int register_android_media_visualizer(JNIEnv *env);
 
 int register_android_media_AudioEffect(JNIEnv *env)
@@ -924,6 +932,11 @@
         goto bail;
     }
 
+    if (register_android_media_StreamDefaultEffect(env) < 0) {
+        ALOGE("ERROR: StreamDefaultEffect native registration failed\n");
+        goto bail;
+    }
+
     if (register_android_media_visualizer(env) < 0) {
         ALOGE("ERROR: Visualizer native registration failed\n");
         goto bail;
@@ -935,4 +948,3 @@
 bail:
     return result;
 }
-
diff --git a/media/jni/audioeffect/android_media_AudioEffect.h b/media/jni/audioeffect/android_media_AudioEffect.h
new file mode 100644
index 0000000..1d7d75d
--- /dev/null
+++ b/media/jni/audioeffect/android_media_AudioEffect.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#ifndef _ANDROID_MEDIA_AUDIO_EFFECT_H_
+#define _ANDROID_MEDIA_AUDIO_EFFECT_H_
+
+#include <jni.h>
+
+namespace android {
+
+class AudioEffectJni {
+public:
+    // Convert from native error code to AudioEffect.java error code.
+    static jint translateNativeErrorToJava(int code);
+private:
+    // Static methods only, so private constructor.
+    AudioEffectJni() = default;
+};
+
+} // namespace android
+
+#endif // _ANDROID_MEDIA_AUDIO_EFFECT_H_
diff --git a/media/jni/audioeffect/android_media_StreamDefaultEffect.cpp b/media/jni/audioeffect/android_media_StreamDefaultEffect.cpp
new file mode 100644
index 0000000..accdddb
--- /dev/null
+++ b/media/jni/audioeffect/android_media_StreamDefaultEffect.cpp
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "StreamDefaultEffect-JNI"
+
+#include <utils/Errors.h>
+#include <utils/Log.h>
+#include <jni.h>
+#include <nativehelper/JNIHelp.h>
+#include <android_runtime/AndroidRuntime.h>
+#include "media/AudioEffect.h"
+
+#include <nativehelper/ScopedUtfChars.h>
+
+#include "android_media_AudioEffect.h"
+
+using namespace android;
+
+static const char* const kClassPathName = "android/media/audiofx/StreamDefaultEffect";
+
+static jint android_media_StreamDefaultEffect_native_setup(JNIEnv *env,
+                                                           jobject /*thiz*/,
+                                                           jstring type,
+                                                           jstring uuid,
+                                                           jint priority,
+                                                           jint streamUsage,
+                                                           jstring opPackageName,
+                                                           jintArray jId)
+{
+    ALOGV("android_media_StreamDefaultEffect_native_setup");
+    status_t lStatus = NO_ERROR;
+    jint* nId = NULL;
+    const char *typeStr = NULL;
+    const char *uuidStr = NULL;
+
+    ScopedUtfChars opPackageNameStr(env, opPackageName);
+
+    if (type != NULL) {
+        typeStr = env->GetStringUTFChars(type, NULL);
+        if (typeStr == NULL) {  // Out of memory
+            lStatus = NO_MEMORY;
+            jniThrowException(env, "java/lang/RuntimeException", "Out of memory");
+            goto setup_exit;
+        }
+    }
+
+    if (uuid != NULL) {
+        uuidStr = env->GetStringUTFChars(uuid, NULL);
+        if (uuidStr == NULL) {  // Out of memory
+            lStatus = NO_MEMORY;
+            jniThrowException(env, "java/lang/RuntimeException", "Out of memory");
+            goto setup_exit;
+        }
+    }
+
+    if (typeStr == NULL && uuidStr == NULL) {
+        lStatus = BAD_VALUE;
+        goto setup_exit;
+    }
+
+    nId = reinterpret_cast<jint *>(env->GetPrimitiveArrayCritical(jId, NULL));
+    if (nId == NULL) {
+        ALOGE("setup: Error retrieving id pointer");
+        lStatus = BAD_VALUE;
+        goto setup_exit;
+    }
+
+    // create the native StreamDefaultEffect.
+    audio_unique_id_t id;
+    lStatus = AudioEffect::addStreamDefaultEffect(typeStr,
+                                                  String16(opPackageNameStr.c_str()),
+                                                  uuidStr,
+                                                  priority,
+                                                  static_cast<audio_usage_t>(streamUsage),
+                                                  &id);
+    if (lStatus != NO_ERROR) {
+        ALOGE("setup: Error adding StreamDefaultEffect");
+        goto setup_exit;
+    }
+
+    nId[0] = static_cast<jint>(id);
+
+setup_exit:
+    // Final cleanup and return.
+
+    if (nId != NULL) {
+        env->ReleasePrimitiveArrayCritical(jId, nId, 0);
+        nId = NULL;
+    }
+
+    if (uuidStr != NULL) {
+        env->ReleaseStringUTFChars(uuid, uuidStr);
+        uuidStr = NULL;
+    }
+
+    if (typeStr != NULL) {
+        env->ReleaseStringUTFChars(type, typeStr);
+        typeStr = NULL;
+    }
+
+    return AudioEffectJni::translateNativeErrorToJava(lStatus);
+}
+
+static void android_media_StreamDefaultEffect_native_release(JNIEnv */*env*/,
+                                                             jobject /*thiz*/,
+                                                             jint id) {
+    status_t lStatus = AudioEffect::removeStreamDefaultEffect(id);
+    if (lStatus != NO_ERROR) {
+        ALOGW("Error releasing StreamDefaultEffect: %d", lStatus);
+    }
+}
+
+// ----------------------------------------------------------------------------
+
+// Dalvik VM type signatures
+static const JNINativeMethod gMethods[] = {
+    {"native_setup",         "(Ljava/lang/String;Ljava/lang/String;IILjava/lang/String;[I)I",
+                                         (void *)android_media_StreamDefaultEffect_native_setup},
+    {"native_release",       "(I)V",      (void *)android_media_StreamDefaultEffect_native_release},
+};
+
+
+// ----------------------------------------------------------------------------
+
+int register_android_media_StreamDefaultEffect(JNIEnv *env)
+{
+    return AndroidRuntime::registerNativeMethods(env, kClassPathName, gMethods, NELEM(gMethods));
+}
diff --git a/packages/ExtServices/src/android/ext/services/notification/Assistant.java b/packages/ExtServices/src/android/ext/services/notification/Assistant.java
index a539b1f..9d19898 100644
--- a/packages/ExtServices/src/android/ext/services/notification/Assistant.java
+++ b/packages/ExtServices/src/android/ext/services/notification/Assistant.java
@@ -17,15 +17,16 @@
 package android.ext.services.notification;
 
 import static android.app.NotificationManager.IMPORTANCE_MIN;
-import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE;
+import static android.service.notification.NotificationListenerService.Ranking
+        .USER_SENTIMENT_NEGATIVE;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.app.INotificationManager;
 import android.app.Notification;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.database.ContentObserver;
-import android.ext.services.R;
 import android.net.Uri;
 import android.os.AsyncTask;
 import android.os.Bundle;
@@ -193,23 +194,27 @@
         if (DEBUG) Log.i(TAG, "ENQUEUED " + sbn.getKey());
         ArrayList<Notification.Action> actions =
                 mSmartActionsHelper.suggestActions(this, sbn);
-        if (actions.isEmpty()) {
-            return null;
-        }
-        return createEnqueuedNotificationAdjustment(sbn, actions);
+        ArrayList<CharSequence> replies = mSmartActionsHelper.suggestReplies(this, sbn);
+        return createEnqueuedNotificationAdjustment(sbn, actions, replies);
     }
 
     /** A convenience helper for creating an adjustment for an SBN. */
+    @Nullable
     private Adjustment createEnqueuedNotificationAdjustment(
             @NonNull StatusBarNotification statusBarNotification,
-            @NonNull ArrayList<Notification.Action> smartActions) {
+            @NonNull ArrayList<Notification.Action> smartActions,
+            @NonNull ArrayList<CharSequence> smartReplies) {
+        if (smartActions.isEmpty() && smartReplies.isEmpty()) {
+            return null;
+        }
         Bundle signals = new Bundle();
         signals.putParcelableArrayList(Adjustment.KEY_SMART_ACTIONS, smartActions);
+        signals.putCharSequenceArrayList(Adjustment.KEY_SMART_REPLIES, smartReplies);
         return new Adjustment(
                 statusBarNotification.getPackageName(),
                 statusBarNotification.getKey(),
                 signals,
-                "smart action" /* explanation */,
+                "smart action, reply" /* explanation */,
                 statusBarNotification.getUserId());
     }
 
diff --git a/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java b/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java
index ed5cbab..9d33bd9 100644
--- a/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java
+++ b/packages/ExtServices/src/android/ext/services/notification/SmartActionsHelper.java
@@ -24,6 +24,7 @@
 import android.os.Bundle;
 import android.os.Parcelable;
 import android.os.Process;
+import android.os.SystemProperties;
 import android.service.notification.StatusBarNotification;
 import android.text.TextUtils;
 import android.util.ArrayMap;
@@ -32,13 +33,13 @@
 import android.view.textclassifier.TextClassifier;
 import android.view.textclassifier.TextLinks;
 
-import com.android.internal.util.Preconditions;
-
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 
 public class SmartActionsHelper {
-    private static final ArrayList<Notification.Action> EMPTY_LIST = new ArrayList<>();
+    private static final ArrayList<Notification.Action> EMPTY_ACTION_LIST = new ArrayList<>();
+    private static final ArrayList<CharSequence> EMPTY_REPLY_LIST = new ArrayList<>();
 
     // If a notification has any of these flags set, it's inelgibile for actions being added.
     private static final int FLAG_MASK_INELGIBILE_FOR_ACTIONS =
@@ -49,6 +50,10 @@
     private static final int MAX_ACTION_EXTRACTION_TEXT_LENGTH = 400;
     private static final int MAX_ACTIONS_PER_LINK = 1;
     private static final int MAX_SMART_ACTIONS = Notification.MAX_ACTION_BUTTONS;
+    // Allow us to test out smart reply with dumb suggestions, it is disabled by default.
+    // TODO: Removed this once we have the model.
+    private static final String SYS_PROP_SMART_REPLIES_EXPERIMENT =
+            "persist.sys.smart_replies_experiment";
 
     SmartActionsHelper() {}
 
@@ -62,14 +67,14 @@
     ArrayList<Notification.Action> suggestActions(
             @Nullable Context context, @NonNull StatusBarNotification sbn) {
         if (!isEligibleForActionAdjustment(sbn)) {
-            return EMPTY_LIST;
+            return EMPTY_ACTION_LIST;
         }
         if (context == null) {
-            return EMPTY_LIST;
+            return EMPTY_ACTION_LIST;
         }
         TextClassificationManager tcm = context.getSystemService(TextClassificationManager.class);
         if (tcm == null) {
-            return EMPTY_LIST;
+            return EMPTY_ACTION_LIST;
         }
         Notification.Action[] actions = sbn.getNotification().actions;
         int numOfExistingActions = actions == null ? 0: actions.length;
@@ -79,6 +84,18 @@
                 getMostSalientActionText(sbn.getNotification()), maxSmartActions);
     }
 
+    ArrayList<CharSequence> suggestReplies(
+            @Nullable Context context, @NonNull StatusBarNotification sbn) {
+        if (!isEligibleForReplyAdjustment(sbn)) {
+            return EMPTY_REPLY_LIST;
+        }
+        if (context == null) {
+            return EMPTY_REPLY_LIST;
+        }
+        // TODO: replaced this with our model when it is ready.
+        return new ArrayList<>(Arrays.asList("Yes, please", "No, thanks"));
+    }
+
     /**
      * Returns whether a notification is eligible for action adjustments.
      *
@@ -108,6 +125,17 @@
                 || hasInlineReply(notification);
     }
 
+    private boolean isEligibleForReplyAdjustment(@NonNull StatusBarNotification sbn) {
+        if (!SystemProperties.getBoolean(SYS_PROP_SMART_REPLIES_EXPERIMENT, false)) {
+            return false;
+        }
+        Notification notification = sbn.getNotification();
+        if (notification.actions == null) {
+            return false;
+        }
+        return hasInlineReply(sbn.getNotification());
+    }
+
     private boolean hasInlineReply(Notification notification) {
         Notification.Action[] actions = notification.actions;
         if (actions == null) {
@@ -151,7 +179,7 @@
             @NonNull TextClassificationManager tcm, @Nullable CharSequence text,
             int maxSmartActions) {
         if (TextUtils.isEmpty(text)) {
-            return EMPTY_LIST;
+            return EMPTY_ACTION_LIST;
         }
         TextClassifier textClassifier = tcm.getTextClassifier();
 
diff --git a/packages/PackageInstaller/.gitignore b/packages/PackageInstaller/.gitignore
deleted file mode 100644
index 7866e9e..0000000
--- a/packages/PackageInstaller/.gitignore
+++ /dev/null
@@ -1,6 +0,0 @@
-bin/
-gen/
-*.iml
-.project
-.classpath
-project.properties
diff --git a/packages/PackageInstaller/Android.bp b/packages/PackageInstaller/Android.bp
new file mode 100644
index 0000000..bc06cab
--- /dev/null
+++ b/packages/PackageInstaller/Android.bp
@@ -0,0 +1,14 @@
+android_app {
+    name: "PackageInstaller",
+
+    srcs: ["src/**/*.java"],
+
+    static_libs: [
+        "androidx.leanback_leanback",
+        "xz-java",
+    ],
+
+    certificate: "platform",
+    privileged: true,
+    platform_apis: true,
+}
\ No newline at end of file
diff --git a/packages/PackageInstaller/Android.mk b/packages/PackageInstaller/Android.mk
deleted file mode 100644
index f556b48..0000000
--- a/packages/PackageInstaller/Android.mk
+++ /dev/null
@@ -1,24 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_USE_AAPT2 := true
-
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_SRC_FILES := \
-    $(call all-java-files-under, src)
-
-LOCAL_STATIC_ANDROID_LIBRARIES += \
-    androidx.leanback_leanback
-
-LOCAL_STATIC_JAVA_LIBRARIES := \
-    xz-java \
-    androidx.annotation_annotation
-
-LOCAL_PACKAGE_NAME := PackageInstaller
-
-LOCAL_CERTIFICATE := platform
-LOCAL_PRIVILEGED_MODULE := true
-LOCAL_PRIVATE_PLATFORM_APIS := true
-
-include $(BUILD_PACKAGE)
diff --git a/packages/PackageInstaller/AndroidManifest.xml b/packages/PackageInstaller/AndroidManifest.xml
index f4b9cef..513c862 100644
--- a/packages/PackageInstaller/AndroidManifest.xml
+++ b/packages/PackageInstaller/AndroidManifest.xml
@@ -17,6 +17,7 @@
 
     <application android:name=".PackageInstallerApplication"
             android:label="@string/app_name"
+            android:icon="@drawable/ic_app_icon"
             android:allowBackup="false"
             android:theme="@style/DialogWhenLarge"
             android:supportsRtl="true"
diff --git a/packages/PackageInstaller/res/drawable/app_icon_foreground.xml b/packages/PackageInstaller/res/drawable/app_icon_foreground.xml
new file mode 100644
index 0000000..b1f40c1
--- /dev/null
+++ b/packages/PackageInstaller/res/drawable/app_icon_foreground.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2018 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.
+  -->
+<inset
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:insetTop="12dp"
+    android:insetRight="12dp"
+    android:insetBottom="12dp"
+    android:insetLeft="12dp">
+
+    <vector
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24"
+        android:viewportHeight="24">
+
+        <path
+            android:fillColor="#FFFFFF"
+            android:pathData="M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z" />
+        <path
+            android:pathData="M0 0h24v24H0z" />
+    </vector>
+</inset>
diff --git a/packages/PackageInstaller/res/drawable/ic_app_icon.xml b/packages/PackageInstaller/res/drawable/ic_app_icon.xml
new file mode 100644
index 0000000..82c18e0
--- /dev/null
+++ b/packages/PackageInstaller/res/drawable/ic_app_icon.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2018 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.
+  -->
+
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+    <background android:drawable="@*android:color/accent_device_default_light"/>
+    <foreground android:drawable="@drawable/app_icon_foreground"/>
+</adaptive-icon>
\ No newline at end of file
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/EventResultPersister.java b/packages/PackageInstaller/src/com/android/packageinstaller/EventResultPersister.java
index 0e62889..3a94fdc 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/EventResultPersister.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/EventResultPersister.java
@@ -16,12 +16,12 @@
 
 package com.android.packageinstaller;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageInstaller;
 import android.os.AsyncTask;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
 import android.util.AtomicFile;
 import android.util.Log;
 import android.util.SparseArray;
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/InstallEventReceiver.java b/packages/PackageInstaller/src/com/android/packageinstaller/InstallEventReceiver.java
index b7d32e9..c70d7db 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/InstallEventReceiver.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/InstallEventReceiver.java
@@ -16,10 +16,10 @@
 
 package com.android.packageinstaller;
 
+import android.annotation.NonNull;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
-import androidx.annotation.NonNull;
 
 /**
  * Receives install events and perists them using a {@link EventResultPersister}.
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/InstallFailed.java b/packages/PackageInstaller/src/com/android/packageinstaller/InstallFailed.java
index 56dc71d..5ba2d32 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/InstallFailed.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/InstallFailed.java
@@ -16,6 +16,7 @@
 
 package com.android.packageinstaller;
 
+import android.annotation.Nullable;
 import android.app.Activity;
 import android.app.AlertDialog;
 import android.app.Dialog;
@@ -28,7 +29,6 @@
 import android.content.pm.PackageManager;
 import android.net.Uri;
 import android.os.Bundle;
-import androidx.annotation.Nullable;
 import android.util.Log;
 import android.widget.TextView;
 
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java b/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java
index 22b1336..c2dd740 100755
--- a/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java
@@ -18,6 +18,7 @@
 
 import static android.content.pm.PackageInstaller.SessionParams.UID_UNKNOWN;
 
+import android.annotation.Nullable;
 import android.app.Activity;
 import android.app.PendingIntent;
 import android.content.Intent;
@@ -32,8 +33,6 @@
 import android.widget.Button;
 import android.widget.ProgressBar;
 
-import androidx.annotation.Nullable;
-
 import com.android.internal.content.PackageHelper;
 
 import java.io.File;
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/InstallStaging.java b/packages/PackageInstaller/src/com/android/packageinstaller/InstallStaging.java
index bf72b65..1bc9dbd 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/InstallStaging.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/InstallStaging.java
@@ -16,6 +16,7 @@
 
 package com.android.packageinstaller;
 
+import android.annotation.Nullable;
 import android.app.Activity;
 import android.app.AlertDialog;
 import android.app.Dialog;
@@ -27,7 +28,6 @@
 import android.net.Uri;
 import android.os.AsyncTask;
 import android.os.Bundle;
-import androidx.annotation.Nullable;
 import android.util.Log;
 
 import java.io.File;
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java b/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java
index 96c6c2f..b3f1105 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java
@@ -19,6 +19,7 @@
 import static com.android.packageinstaller.PackageUtil.getMaxTargetSdkVersionForUid;
 
 import android.Manifest;
+import android.annotation.Nullable;
 import android.app.Activity;
 import android.app.ActivityManager;
 import android.app.AppGlobals;
@@ -35,8 +36,6 @@
 import android.os.RemoteException;
 import android.util.Log;
 
-import androidx.annotation.Nullable;
-
 /**
  * Select which activity is the first visible activity of the installation and forward the intent to
  * it.
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/InstallSuccess.java b/packages/PackageInstaller/src/com/android/packageinstaller/InstallSuccess.java
index ae902d3..94f6b31 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/InstallSuccess.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/InstallSuccess.java
@@ -16,6 +16,7 @@
 
 package com.android.packageinstaller;
 
+import android.annotation.Nullable;
 import android.app.Activity;
 import android.content.ActivityNotFoundException;
 import android.content.Intent;
@@ -24,7 +25,6 @@
 import android.content.pm.ResolveInfo;
 import android.net.Uri;
 import android.os.Bundle;
-import androidx.annotation.Nullable;
 import android.util.Log;
 import android.widget.Button;
 
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/permission/ui/OverlayTouchActivity.java b/packages/PackageInstaller/src/com/android/packageinstaller/OverlayTouchActivity.java
similarity index 85%
rename from packages/PackageInstaller/src/com/android/packageinstaller/permission/ui/OverlayTouchActivity.java
rename to packages/PackageInstaller/src/com/android/packageinstaller/OverlayTouchActivity.java
index 8aa158b..1fdbd97 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/permission/ui/OverlayTouchActivity.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/OverlayTouchActivity.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 The Android Open Source Project
+ * Copyright (C) 2018 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.
@@ -13,14 +13,14 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android.packageinstaller.permission.ui;
+package com.android.packageinstaller;
 
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
 
 import android.app.Activity;
 import android.os.Bundle;
 
-public class OverlayTouchActivity extends Activity {
+class OverlayTouchActivity extends Activity {
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         getWindow().addPrivateFlags(PRIVATE_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
index 83c91ea..97bafe7 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
@@ -17,6 +17,8 @@
 package com.android.packageinstaller;
 
 import android.Manifest;
+import android.annotation.NonNull;
+import android.annotation.StringRes;
 import android.app.AlertDialog;
 import android.app.AppGlobals;
 import android.app.AppOpsManager;
@@ -47,11 +49,6 @@
 import android.widget.Button;
 import android.widget.TextView;
 
-import androidx.annotation.NonNull;
-import androidx.annotation.StringRes;
-
-import com.android.packageinstaller.permission.ui.OverlayTouchActivity;
-
 import java.io.File;
 
 /**
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/PackageUtil.java b/packages/PackageInstaller/src/com/android/packageinstaller/PackageUtil.java
index 68daed7..ba4bf8a 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/PackageUtil.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/PackageUtil.java
@@ -17,6 +17,8 @@
 
 package com.android.packageinstaller;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.app.Activity;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
@@ -27,8 +29,6 @@
 import android.content.res.Resources;
 import android.graphics.drawable.Drawable;
 import android.os.UserHandle;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
 import android.util.Log;
 import android.view.View;
 import android.widget.ImageView;
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/TemporaryFileManager.java b/packages/PackageInstaller/src/com/android/packageinstaller/TemporaryFileManager.java
index 66e93b3..f77318c 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/TemporaryFileManager.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/TemporaryFileManager.java
@@ -16,11 +16,11 @@
 
 package com.android.packageinstaller;
 
+import android.annotation.NonNull;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
 import android.os.SystemClock;
-import androidx.annotation.NonNull;
 import android.util.Log;
 
 import java.io.File;
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/UninstallEventReceiver.java b/packages/PackageInstaller/src/com/android/packageinstaller/UninstallEventReceiver.java
index ad3cd4c..c3e9c23 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/UninstallEventReceiver.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/UninstallEventReceiver.java
@@ -16,10 +16,10 @@
 
 package com.android.packageinstaller;
 
+import android.annotation.NonNull;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
-import androidx.annotation.NonNull;
 
 /**
  * Receives uninstall events and persists them using a {@link EventResultPersister}.
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/UninstallFinish.java b/packages/PackageInstaller/src/com/android/packageinstaller/UninstallFinish.java
index 8282c83..5a51ac2 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/UninstallFinish.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/UninstallFinish.java
@@ -16,6 +16,7 @@
 
 package com.android.packageinstaller;
 
+import android.annotation.NonNull;
 import android.app.Notification;
 import android.app.NotificationChannel;
 import android.app.NotificationManager;
@@ -35,7 +36,6 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.provider.Settings;
-import androidx.annotation.NonNull;
 import android.util.Log;
 import android.widget.Toast;
 
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/UninstallUninstalling.java b/packages/PackageInstaller/src/com/android/packageinstaller/UninstallUninstalling.java
index e27186b..1c0aec1 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/UninstallUninstalling.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/UninstallUninstalling.java
@@ -16,6 +16,7 @@
 
 package com.android.packageinstaller;
 
+import android.annotation.Nullable;
 import android.app.Activity;
 import android.app.ActivityThread;
 import android.app.AlertDialog;
@@ -34,7 +35,6 @@
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.UserHandle;
-import androidx.annotation.Nullable;
 import android.util.Log;
 import android.widget.Toast;
 
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java b/packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java
index d5df9f5..1a01dc0 100755
--- a/packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java
@@ -21,6 +21,8 @@
 import static com.android.packageinstaller.PackageUtil.getMaxTargetSdkVersionForUid;
 
 import android.Manifest;
+import android.annotation.NonNull;
+import android.annotation.StringRes;
 import android.app.Activity;
 import android.app.ActivityManager;
 import android.app.ActivityThread;
@@ -52,8 +54,6 @@
 import android.os.ServiceManager;
 import android.os.UserHandle;
 import android.os.UserManager;
-import androidx.annotation.NonNull;
-import androidx.annotation.StringRes;
 import android.util.Log;
 
 import com.android.packageinstaller.handheld.ErrorDialogFragment;
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/television/UninstallAppProgressFragment.java b/packages/PackageInstaller/src/com/android/packageinstaller/television/UninstallAppProgressFragment.java
index 7402a7d..af6d9c5 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/television/UninstallAppProgressFragment.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/television/UninstallAppProgressFragment.java
@@ -16,11 +16,11 @@
 
 package com.android.packageinstaller.television;
 
+import android.annotation.Nullable;
 import android.app.Fragment;
 import android.content.Intent;
 import android.os.Bundle;
 import android.provider.Settings;
-import androidx.annotation.Nullable;
 import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.View;
diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml
index 50d9c17..d30be58 100644
--- a/packages/SettingsLib/res/values-ca/strings.xml
+++ b/packages/SettingsLib/res/values-ca/strings.xml
@@ -189,9 +189,9 @@
     <string name="vpn_settings_not_available" msgid="956841430176985598">"La configuració de la VPN no està disponible per a aquest usuari."</string>
     <string name="tethering_settings_not_available" msgid="6765770438438291012">"La configuració de compartició de xarxa no està disponible per a aquest usuari."</string>
     <string name="apn_settings_not_available" msgid="7873729032165324000">"La configuració del nom del punt d\'accés no està disponible per a aquest usuari."</string>
-    <string name="enable_adb" msgid="7982306934419797485">"Depuració USB"</string>
+    <string name="enable_adb" msgid="7982306934419797485">"Depuració per USB"</string>
     <string name="enable_adb_summary" msgid="4881186971746056635">"Activa el mode de depuració quan el dispositiu estigui connectat per USB"</string>
-    <string name="clear_adb_keys" msgid="4038889221503122743">"Revoca autoritzacions de depuració USB"</string>
+    <string name="clear_adb_keys" msgid="4038889221503122743">"Revoca autoritzacions de depuració per USB"</string>
     <string name="bugreport_in_power" msgid="7923901846375587241">"Drecera per a informe d\'errors"</string>
     <string name="bugreport_in_power_summary" msgid="1778455732762984579">"Mostra un botó al menú d\'engegada per crear un informe d\'errors"</string>
     <string name="keep_screen_on" msgid="1146389631208760344">"Pantalla sempre activa"</string>
@@ -251,9 +251,9 @@
     <string name="debug_view_attributes" msgid="6485448367803310384">"Inspecció d\'atributs de visualització"</string>
     <string name="mobile_data_always_on_summary" msgid="8149773901431697910">"Mantén les dades mòbils sempre actives, fins i tot quan la Wi‑Fi està activada (per canviar de xarxa ràpidament)."</string>
     <string name="tethering_hardware_offload_summary" msgid="7726082075333346982">"Fes servir l\'acceleració per maquinari per compartir la xarxa, si està disponible"</string>
-    <string name="adb_warning_title" msgid="6234463310896563253">"Voleu permetre la depuració USB?"</string>
-    <string name="adb_warning_message" msgid="7316799925425402244">"La depuració USB només està indicada per a activitats de desenvolupament. Fes-la servir intercanviar dades entre l\'ordinador i el dispositiu, per instal·lar aplicacions al dispositiu sense rebre notificacions i per llegir dades de registre."</string>
-    <string name="adb_keys_warning_message" msgid="5659849457135841625">"Vols revocar l\'accés a la depuració d\'USB dels ordinadors que has autoritzat anteriorment?"</string>
+    <string name="adb_warning_title" msgid="6234463310896563253">"Voleu permetre la depuració per USB?"</string>
+    <string name="adb_warning_message" msgid="7316799925425402244">"La depuració per USB només està indicada per a activitats de desenvolupament. Fes-la servir intercanviar dades entre l\'ordinador i el dispositiu, per instal·lar aplicacions al dispositiu sense rebre notificacions i per llegir dades de registre."</string>
+    <string name="adb_keys_warning_message" msgid="5659849457135841625">"Vols revocar l\'accés a la depuració per USB dels ordinadors que has autoritzat anteriorment?"</string>
     <string name="dev_settings_warning_title" msgid="7244607768088540165">"Vols permetre la conf. de desenvolupament?"</string>
     <string name="dev_settings_warning_message" msgid="2298337781139097964">"Aquesta configuració només està prevista per a usos de desenvolupament. Pot fer que el dispositiu i que les aplicacions s\'interrompin o tinguin un comportament inadequat."</string>
     <string name="verify_apps_over_usb_title" msgid="4177086489869041953">"Verifica aplicacions per USB"</string>
diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml
index 1579d8f..a90231a 100644
--- a/packages/SettingsLib/res/values-cs/strings.xml
+++ b/packages/SettingsLib/res/values-cs/strings.xml
@@ -189,9 +189,9 @@
     <string name="vpn_settings_not_available" msgid="956841430176985598">"Nastavení sítě VPN pro tohoto uživatele není dostupné."</string>
     <string name="tethering_settings_not_available" msgid="6765770438438291012">"Nastavení sdíleného připojení pro tohoto uživatele není dostupné."</string>
     <string name="apn_settings_not_available" msgid="7873729032165324000">"Nastavení přístupového bodu pro tohoto uživatele není dostupné."</string>
-    <string name="enable_adb" msgid="7982306934419797485">"Ladění USB"</string>
+    <string name="enable_adb" msgid="7982306934419797485">"Ladění přes USB"</string>
     <string name="enable_adb_summary" msgid="4881186971746056635">"Povolit režim ladění s připojeným zařízením USB"</string>
-    <string name="clear_adb_keys" msgid="4038889221503122743">"Zrušit autorizace k ladění USB"</string>
+    <string name="clear_adb_keys" msgid="4038889221503122743">"Zrušit autorizace k ladění přes USB"</string>
     <string name="bugreport_in_power" msgid="7923901846375587241">"Zástupce hlášení chyb"</string>
     <string name="bugreport_in_power_summary" msgid="1778455732762984579">"Zobrazit v hlavní nabídce tlačítko k vygenerování chybového hlášení"</string>
     <string name="keep_screen_on" msgid="1146389631208760344">"Nevypínat obrazovku"</string>
@@ -251,9 +251,9 @@
     <string name="debug_view_attributes" msgid="6485448367803310384">"Kontrola atributu zobrazení"</string>
     <string name="mobile_data_always_on_summary" msgid="8149773901431697910">"Mobilní data budou vždy ponechána aktivní, i když bude aktivní Wi-Fi (za účelem rychlého přepínání sítí)."</string>
     <string name="tethering_hardware_offload_summary" msgid="7726082075333346982">"Pokud je k dispozici hardwarová akceleraci tetheringu, použít ji"</string>
-    <string name="adb_warning_title" msgid="6234463310896563253">"Povolit ladění USB?"</string>
+    <string name="adb_warning_title" msgid="6234463310896563253">"Povolit ladění přes USB?"</string>
     <string name="adb_warning_message" msgid="7316799925425402244">"Ladění prostřednictvím rozhraní USB je určeno pouze pro účely vývoje. Použijte je ke kopírování dat mezi počítačem a zařízením, instalaci aplikací do zařízení bez upozornění a čtení dat protokolů."</string>
-    <string name="adb_keys_warning_message" msgid="5659849457135841625">"Zrušit přístup k ladění USB ze všech počítačů, které jste v minulosti autorizovali?"</string>
+    <string name="adb_keys_warning_message" msgid="5659849457135841625">"Zrušit přístup k ladění přes USB ze všech počítačů, které jste v minulosti autorizovali?"</string>
     <string name="dev_settings_warning_title" msgid="7244607768088540165">"Povolit nastavení pro vývojáře?"</string>
     <string name="dev_settings_warning_message" msgid="2298337781139097964">"Tato nastavení jsou určena pouze pro vývojáře. Mohou způsobit rozbití nebo nesprávné fungování zařízení a nainstalovaných aplikací."</string>
     <string name="verify_apps_over_usb_title" msgid="4177086489869041953">"Ověřit aplikace z USB"</string>
diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml
index ec7bf98..0410977 100644
--- a/packages/SettingsLib/res/values-eu/strings.xml
+++ b/packages/SettingsLib/res/values-eu/strings.xml
@@ -165,7 +165,7 @@
     <string name="tts_engine_settings_title" msgid="3499112142425680334">"<xliff:g id="TTS_ENGINE_NAME">%s</xliff:g> motorraren ezarpenak"</string>
     <string name="tts_engine_settings_button" msgid="1030512042040722285">"Abiarazi motorraren ezarpenak"</string>
     <string name="tts_engine_preference_section_title" msgid="448294500990971413">"Motor hobetsia"</string>
-    <string name="tts_general_section_title" msgid="4402572014604490502">"Orokorra"</string>
+    <string name="tts_general_section_title" msgid="4402572014604490502">"Orokorrak"</string>
     <string name="tts_reset_speech_pitch_title" msgid="5789394019544785915">"Berrezarri ahots-tonua"</string>
     <string name="tts_reset_speech_pitch_summary" msgid="8700539616245004418">"Berrezarri testua esateko ahots-tonu lehenetsia."</string>
   <string-array name="tts_rate_entries">
diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml
index 5eac8fa..e94dd73 100644
--- a/packages/SettingsLib/res/values-fr/strings.xml
+++ b/packages/SettingsLib/res/values-fr/strings.xml
@@ -371,10 +371,10 @@
     <string name="power_remaining_duration_only_enhanced" msgid="4189311599812296592">"Temps restant en fonction de votre utilisation : environ <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
     <string name="power_discharging_duration_enhanced" msgid="1992003260664804080">"Temps restant en fonction de votre utilisation (<xliff:g id="LEVEL">%2$s</xliff:g>) : environ <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
     <string name="power_remaining_duration_only_short" msgid="3463575350656389957">"Temps restant : <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
-    <string name="power_discharge_by_enhanced" msgid="2095821536747992464">"Devrait durer jusqu\'à environ <xliff:g id="TIME">%1$s</xliff:g> en fonction de l\'utilisation (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
-    <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Devrait durer jusqu\'à environ <xliff:g id="TIME">%1$s</xliff:g> en fonction de l\'utilisation"</string>
-    <string name="power_discharge_by" msgid="6453537733650125582">"Devrait durer jusqu\'à environ <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
-    <string name="power_discharge_by_only" msgid="107616694963545745">"Devrait durer jusqu\'à environ <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="power_discharge_by_enhanced" msgid="2095821536747992464">"Temps restant estimé en fonction de votre utilisation : <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
+    <string name="power_discharge_by_only_enhanced" msgid="2175151772952365149">"Temps restant estimé en fonction de votre utilisation : <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="power_discharge_by" msgid="6453537733650125582">"Temps restant estimé : <xliff:g id="TIME">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
+    <string name="power_discharge_by_only" msgid="107616694963545745">"Temps restant estimé : <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration_only" msgid="5996752448813295329">"Il reste moins de <xliff:g id="THRESHOLD">%1$s</xliff:g>"</string>
     <string name="power_remaining_less_than_duration" msgid="5751885147712659423">"Il reste moins de <xliff:g id="THRESHOLD">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
     <string name="power_remaining_more_than_subtext" msgid="3176771815132876675">"Il reste plus de <xliff:g id="TIME_REMAINING">%1$s</xliff:g> (<xliff:g id="LEVEL">%2$s</xliff:g>)"</string>
diff --git a/packages/SystemUI/README.md b/packages/SystemUI/README.md
index d80e4ff..33c5551 100644
--- a/packages/SystemUI/README.md
+++ b/packages/SystemUI/README.md
@@ -162,7 +162,7 @@
 
 Draws decorations about the screen in software (e.g. rounded corners, cutouts).
 
-### [com.android.systemui.fingerprint.FingerprintDialogImpl](/packages/SystemUI/src/com/android/systemui/fingerprint/FingerprintDialogImpl.java)
+### [com.android.systemui.biometrics.BiometricDialogImpl](/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogImpl.java)
 
 Fingerprint UI.
 
diff --git a/packages/SystemUI/res-keyguard/values-fa/strings.xml b/packages/SystemUI/res-keyguard/values-fa/strings.xml
index 62cd4ef..db7923d 100644
--- a/packages/SystemUI/res-keyguard/values-fa/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-fa/strings.xml
@@ -99,8 +99,8 @@
     <string name="kg_failed_attempts_almost_at_erase_profile" product="default" msgid="2162434417489128282">"<xliff:g id="NUMBER_0">%1$d</xliff:g> تلاش ناموفق برای باز کردن قفل تلفن داشته‌اید. پس از <xliff:g id="NUMBER_1">%2$d</xliff:g> تلاش ناموفق دیگر، نمایه کاری پاک می‌شود که با آن همه داده‌های نمایه حذف می‌شود."</string>
     <string name="kg_failed_attempts_now_erasing_profile" product="tablet" msgid="8966727588974691544">"<xliff:g id="NUMBER">%d</xliff:g> تلاش ناموفق برای باز کردن قفل رایانه لوحی داشته‌اید. نمایه کاری پاک می‌شود که با آن همه داده‌های نمایه حذف می‌شود."</string>
     <string name="kg_failed_attempts_now_erasing_profile" product="default" msgid="8476407539834855">"<xliff:g id="NUMBER">%d</xliff:g> تلاش ناموفق برای باز کردن قفل تلفن داشته‌اید. نمایه کاری پاک می‌شود که با آن همه داده‌های نمایه حذف می‌شود."</string>
-    <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="956706236554092172">"‏شما الگوی باز کردن قفل را <xliff:g id="NUMBER_0">%1$d</xliff:g> بار اشتباه کشیده‎اید. بعد از <xliff:g id="NUMBER_1">%2$d</xliff:g> تلاش ناموفق، از شما خواسته می‎شود که با استفاده از یک حساب رایانامه قفل رایانه لوحی خود را باز کنید.\n\n لطفاً پس از <xliff:g id="NUMBER_2">%3$d</xliff:g> ثانیه دوباره امتحان کنید."</string>
-    <string name="kg_failed_attempts_almost_at_login" product="default" msgid="8364140853305528449">"‏شما الگوی باز کردن قفل را <xliff:g id="NUMBER_0">%1$d</xliff:g> بار اشتباه کشیده‌اید. پس از <xliff:g id="NUMBER_1">%2$d</xliff:g> تلاش ناموفق، از شما خواسته می‎شود که با استفاده از یک حساب رایانامه قفل تلفن را باز کنید.\n\n لطفاً پس از <xliff:g id="NUMBER_2">%3$d</xliff:g> ثانیه دوباره امتحان کنید."</string>
+    <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="956706236554092172">"‏شما الگوی باز کردن قفل را <xliff:g id="NUMBER_0">%1$d</xliff:g> بار اشتباه کشیده‎اید. بعد از <xliff:g id="NUMBER_1">%2$d</xliff:g> تلاش ناموفق، از شما خواسته می‎شود که با استفاده از یک حساب ایمیل قفل رایانه لوحی خود را باز کنید.\n\n لطفاً پس از <xliff:g id="NUMBER_2">%3$d</xliff:g> ثانیه دوباره امتحان کنید."</string>
+    <string name="kg_failed_attempts_almost_at_login" product="default" msgid="8364140853305528449">"‏شما الگوی باز کردن قفل را <xliff:g id="NUMBER_0">%1$d</xliff:g> بار اشتباه کشیده‌اید. پس از <xliff:g id="NUMBER_1">%2$d</xliff:g> تلاش ناموفق، از شما خواسته می‎شود که با استفاده از یک حساب ایمیل قفل تلفن را باز کنید.\n\n لطفاً پس از <xliff:g id="NUMBER_2">%3$d</xliff:g> ثانیه دوباره امتحان کنید."</string>
     <string name="kg_password_wrong_pin_code_pukked" msgid="3389829202093674267">"کد پین سیم‌کارت اشتباه است، اکنون برای باز کردن قفل دستگاهتان باید با شرکت مخابراتی تماس بگیرید."</string>
     <plurals name="kg_password_wrong_pin_code" formatted="false" msgid="4314341367727055967">
       <item quantity="one">کد پین سیم‌کارت اشتباه است، <xliff:g id="NUMBER_1">%d</xliff:g> بار دیگر می‌توانید تلاش کنید.</item>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index 364156a2..9c82b70 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -62,11 +62,11 @@
     <string name="label_view" msgid="6304565553218192990">"Mostra"</string>
     <string name="always_use_device" msgid="4015357883336738417">"Obre sempre <xliff:g id="APPLICATION">%1$s</xliff:g> quan es connecti <xliff:g id="USB_DEVICE">%2$s</xliff:g>"</string>
     <string name="always_use_accessory" msgid="3257892669444535154">"Obre sempre <xliff:g id="APPLICATION">%1$s</xliff:g> quan es connecti <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>"</string>
-    <string name="usb_debugging_title" msgid="4513918393387141949">"Vols permetre la depuració USB?"</string>
+    <string name="usb_debugging_title" msgid="4513918393387141949">"Vols permetre la depuració per USB?"</string>
     <string name="usb_debugging_message" msgid="2220143855912376496">"L\'empremta digital de la clau de l\'RSA de l\'equip és:\n<xliff:g id="FINGERPRINT">%1$s</xliff:g>"</string>
     <string name="usb_debugging_always" msgid="303335496705863070">"Dona sempre permís des d\'aquest equip"</string>
-    <string name="usb_debugging_secondary_user_title" msgid="6353808721761220421">"No es permet la depuració USB"</string>
-    <string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"L\'usuari que té iniciada la sessió al dispositiu en aquest moment no pot activar la depuració USB. Per utilitzar aquesta funció, cal canviar a l\'usuari principal."</string>
+    <string name="usb_debugging_secondary_user_title" msgid="6353808721761220421">"No es permet la depuració per USB"</string>
+    <string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"L\'usuari que té iniciada la sessió al dispositiu en aquest moment no pot activar la depuració per USB. Per utilitzar aquesta funció, cal canviar a l\'usuari principal."</string>
     <string name="compat_mode_on" msgid="6623839244840638213">"Zoom per omplir pantalla"</string>
     <string name="compat_mode_off" msgid="4434467572461327898">"Estira per omplir pant."</string>
     <string name="global_action_screenshot" msgid="8329831278085426283">"Captura de pantalla"</string>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index d72b1d1..5b99dd4 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -64,11 +64,11 @@
     <string name="label_view" msgid="6304565553218192990">"Zobrazit"</string>
     <string name="always_use_device" msgid="4015357883336738417">"Když bude připojeno zařízení <xliff:g id="USB_DEVICE">%2$s</xliff:g>, vždy otevřít <xliff:g id="APPLICATION">%1$s</xliff:g>"</string>
     <string name="always_use_accessory" msgid="3257892669444535154">"Když bude připojeno zařízení <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>, vždy otevřít <xliff:g id="APPLICATION">%1$s</xliff:g>"</string>
-    <string name="usb_debugging_title" msgid="4513918393387141949">"Povolit ladění USB?"</string>
+    <string name="usb_debugging_title" msgid="4513918393387141949">"Povolit ladění přes USB?"</string>
     <string name="usb_debugging_message" msgid="2220143855912376496">"Digitální otisk RSA počítače je:\n<xliff:g id="FINGERPRINT">%1$s</xliff:g>"</string>
     <string name="usb_debugging_always" msgid="303335496705863070">"Vždy povolit z tohoto počítače"</string>
-    <string name="usb_debugging_secondary_user_title" msgid="6353808721761220421">"Ladění USB není povoleno"</string>
-    <string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"Uživatel aktuálně přihlášený k tomuto zařízení nemůže zapnout ladění USB. Chcete-li tuto funkci použít, přepněte na primárního uživatele."</string>
+    <string name="usb_debugging_secondary_user_title" msgid="6353808721761220421">"Ladění přes USB není povoleno"</string>
+    <string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"Uživatel aktuálně přihlášený k tomuto zařízení nemůže zapnout ladění přes USB. Chcete-li tuto funkci použít, přepněte na primárního uživatele."</string>
     <string name="compat_mode_on" msgid="6623839244840638213">"Přiblížit na celou obrazovku"</string>
     <string name="compat_mode_off" msgid="4434467572461327898">"Na celou obrazovku"</string>
     <string name="global_action_screenshot" msgid="8329831278085426283">"Snímek obrazovky"</string>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index 34ab46f..db393c0 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -472,28 +472,28 @@
     <string name="monitoring_description_managed_profile_ca_certificate" msgid="4683248196789897964">"سازمان شما مرجع گواهینامه‌ای در نمایه کاری شما نصب کرده است. ممکن است ترافیک امن شبکه شما پایش یا تغییر داده شود."</string>
     <string name="monitoring_description_ca_certificate" msgid="7886985418413598352">"مرجع گواهینامه‌ای در این دستگاه نصب شده است. ممکن است ترافیک امن شبکه شما پایش یا تغییر داده شود."</string>
     <string name="monitoring_description_management_network_logging" msgid="7184005419733060736">"سرپرست سیستم شما گزارش‌گیری از شبکه را (که ترافیک دستگاه شما را پایش می‌کند) روشن کرده است."</string>
-    <string name="monitoring_description_named_vpn" msgid="7403457334088909254">"به <xliff:g id="VPN_APP">%1$s</xliff:g> متصل شده‌اید، که می‌تواند فعالیت شبکه شما را (ازجمله رایانامه‌‌ها، برنامه‌‌ها و وب‌سایت‌ها) پایش کند."</string>
-    <string name="monitoring_description_two_named_vpns" msgid="4198511413729213802">"به <xliff:g id="VPN_APP_0">%1$s</xliff:g> و <xliff:g id="VPN_APP_1">%2$s</xliff:g> متصل شده‌اید، که می‌توانند فعالیت شما را در شبکه (ازجمله رایانامه‌‌ها، برنامه‌‌ها و وب‌سایت‌ها) پایش کنند."</string>
-    <string name="monitoring_description_managed_profile_named_vpn" msgid="1427905889862420559">"نمایه کاری شما به <xliff:g id="VPN_APP">%1$s</xliff:g> متصل است، که می‌تواند فعالیت شما در شبکه (ازجمله رایانامه‌ها، برنامه‌ها و وب‌سایت‌ها) را پایش کند."</string>
-    <string name="monitoring_description_personal_profile_named_vpn" msgid="3133980926929069283">"نمایه شخصی شما به <xliff:g id="VPN_APP">%1$s</xliff:g> متصل شده‌ است، که می‌تواند فعالیت شما در شبکه (ازجمله رایانامه‌‌ها، برنامه‌‌ها و وب‌سایت‌ها) را پایش کند."</string>
+    <string name="monitoring_description_named_vpn" msgid="7403457334088909254">"به <xliff:g id="VPN_APP">%1$s</xliff:g> متصل شده‌اید، که می‌تواند فعالیت شبکه شما را (ازجمله ایمیل‌ها، برنامه‌‌ها و وب‌سایت‌ها) پایش کند."</string>
+    <string name="monitoring_description_two_named_vpns" msgid="4198511413729213802">"به <xliff:g id="VPN_APP_0">%1$s</xliff:g> و <xliff:g id="VPN_APP_1">%2$s</xliff:g> متصل شده‌اید، که می‌توانند فعالیت شما را در شبکه (ازجمله ایمیل‌ها، برنامه‌‌ها و وب‌سایت‌ها) پایش کنند."</string>
+    <string name="monitoring_description_managed_profile_named_vpn" msgid="1427905889862420559">"نمایه کاری شما به <xliff:g id="VPN_APP">%1$s</xliff:g> متصل است، که می‌تواند فعالیت شما در شبکه (ازجمله ایمیل‌ها، برنامه‌ها و وب‌سایت‌ها) را پایش کند."</string>
+    <string name="monitoring_description_personal_profile_named_vpn" msgid="3133980926929069283">"نمایه شخصی شما به <xliff:g id="VPN_APP">%1$s</xliff:g> متصل شده‌ است، که می‌تواند فعالیت شما در شبکه (ازجمله ایمیل‌ها، برنامه‌‌ها و وب‌سایت‌ها) را پایش کند."</string>
     <string name="monitoring_description_do_header_generic" msgid="96588491028288691">"<xliff:g id="DEVICE_OWNER_APP">%1$s</xliff:g> دستگاه شما را مدیریت می‌کند."</string>
     <string name="monitoring_description_do_header_with_name" msgid="5511133708978206460">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> با استفاده از <xliff:g id="DEVICE_OWNER_APP">%2$s</xliff:g> دستگاهتان را مدیریت می‌کند."</string>
     <string name="monitoring_description_do_body" msgid="3639594537660975895">"سرپرست سیستم شما می‌تواند تنظیمات، دسترسی شرکتی، برنامه‌ها، داده‌های مرتبط با دستگاه و اطلاعات مکان دستگاه شما را مدیریت کند و بر آن‌ها نظارت داشته باشد."</string>
     <string name="monitoring_description_do_learn_more_separator" msgid="3785251953067436862">" "</string>
     <string name="monitoring_description_do_learn_more" msgid="1849514470437907421">"بیشتر بدانید"</string>
-    <string name="monitoring_description_do_body_vpn" msgid="8255218762488901796">"به <xliff:g id="VPN_APP">%1$s</xliff:g> وصل شده‌اید، که می‌تواند فعالیت شبکه شما را (ازجمله رایانامه‌‌ها، برنامه‌‌ها و وب‌سایت‌ها) کنترل کند."</string>
+    <string name="monitoring_description_do_body_vpn" msgid="8255218762488901796">"به <xliff:g id="VPN_APP">%1$s</xliff:g> وصل شده‌اید، که می‌تواند فعالیت شبکه شما را (ازجمله ایمیل‌ها، برنامه‌‌ها و وب‌سایت‌ها) کنترل کند."</string>
     <string name="monitoring_description_vpn_settings_separator" msgid="1933186756733474388">" "</string>
     <string name="monitoring_description_vpn_settings" msgid="6434859242636063861">"‏باز کردن تنظیمات VPN"</string>
     <string name="monitoring_description_ca_cert_settings_separator" msgid="4987350385906393626">" "</string>
     <string name="monitoring_description_ca_cert_settings" msgid="5489969458872997092">"باز کردن اعتبارنامه مورداعتماد"</string>
     <string name="monitoring_description_network_logging" msgid="7223505523384076027">"سرپرست سیستم شما گزارش‌گیری شبکه را (که بر ترافیک دستگاهتان نظارت می‌کند) روشن کرده است.\n\nبرای اطلاعات بیشتر، با سرپرست خود تماس بگیرید."</string>
-    <string name="monitoring_description_vpn" msgid="4445150119515393526">"‏شما به برنامه‌ای برای تنظیم اتصال VPN اجازه دادید.\n\n این برنامه می‌تواند دستگاه و فعالیت شبکه‌تان را کنترل کند، از جمله رایانامه‌، برنامه‌ و وب‌سایت‌ها."</string>
-    <string name="monitoring_description_vpn_profile_owned" msgid="2958019119161161530">"‏نمایه کاری شما توسط <xliff:g id="ORGANIZATION">%1$s</xliff:g> مدیریت می‌شود.\n\nسرپرست سیستم شما می‌تواند بر فعالیت شبکه شما (ازجمله رایانامه‌ها، برنامه‌ها و وب‌سایت‌ها) نظارت داشته باشد.\n\nبرای اطلاعات بیشتر، با سرپرست خود تماس بگیرید.\n\nهمچنین به VPN متصل هستید که می‌تواند بر فعالیت شبکه شما نظارت داشته باشد."</string>
+    <string name="monitoring_description_vpn" msgid="4445150119515393526">"‏شما به برنامه‌ای برای تنظیم اتصال VPN اجازه دادید.\n\n این برنامه می‌تواند دستگاه و فعالیت شبکه‌تان را کنترل کند، از جمله ایمیل‌، برنامه‌ و وب‌سایت‌ها."</string>
+    <string name="monitoring_description_vpn_profile_owned" msgid="2958019119161161530">"‏نمایه کاری شما توسط <xliff:g id="ORGANIZATION">%1$s</xliff:g> مدیریت می‌شود.\n\nسرپرست سیستم شما می‌تواند بر فعالیت شبکه شما (ازجمله ایمیل‌ها، برنامه‌ها و وب‌سایت‌ها) نظارت داشته باشد.\n\nبرای اطلاعات بیشتر، با سرپرست خود تماس بگیرید.\n\nهمچنین به VPN متصل هستید که می‌تواند بر فعالیت شبکه شما نظارت داشته باشد."</string>
     <string name="legacy_vpn_name" msgid="6604123105765737830">"VPN"</string>
-    <string name="monitoring_description_app" msgid="1828472472674709532">"شما به <xliff:g id="APPLICATION">%1$s</xliff:g> متصل هستید، که می‌تواند فعالیت شما در شبکه (ازجمله رایانامه‌، برنامه‌ و وب‌سایت‌ها) را پایش کند."</string>
-    <string name="monitoring_description_app_personal" msgid="484599052118316268">"شما به <xliff:g id="APPLICATION">%1$s</xliff:g> وصل شده‌اید، که می‌تواند فعالیت شبکه شخصی شما از جمله رایانامه‌، برنامه‌ و وب‌سایت‌ها را کنترل کند."</string>
-    <string name="branded_monitoring_description_app_personal" msgid="2669518213949202599">"به <xliff:g id="APPLICATION">%1$s</xliff:g> وصل شده‌اید، که می‌تواند فعالیت شبکه شخصی شما را (ازجمله رایانامه‌‌ها، برنامه‌‌ها و وب‌سایت‌ها) کنترل کند."</string>
-    <string name="monitoring_description_app_work" msgid="4612997849787922906">"نمایه کاری شما توسط <xliff:g id="ORGANIZATION">%1$s</xliff:g> مدیریت می‌شود. این نمایه به <xliff:g id="APPLICATION">%2$s</xliff:g> متصل است که می‌تواند فعالیت شما در شبکه (ازجمله رایانامه‌ها، برنامه‌ها و وب‌سایت‌ها) را پایش کند.\n\nبرای اطلاعات بیشتر، با سرپرست سیستم تماس بگیرید."</string>
+    <string name="monitoring_description_app" msgid="1828472472674709532">"شما به <xliff:g id="APPLICATION">%1$s</xliff:g> متصل هستید، که می‌تواند فعالیت شما در شبکه (ازجمله ایمیل‌، برنامه‌ و وب‌سایت‌ها) را پایش کند."</string>
+    <string name="monitoring_description_app_personal" msgid="484599052118316268">"شما به <xliff:g id="APPLICATION">%1$s</xliff:g> وصل شده‌اید، که می‌تواند فعالیت شبکه شخصی شما از جمله ایمیل‌، برنامه‌ و وب‌سایت‌ها را کنترل کند."</string>
+    <string name="branded_monitoring_description_app_personal" msgid="2669518213949202599">"به <xliff:g id="APPLICATION">%1$s</xliff:g> وصل شده‌اید که می‌تواند فعالیت شبکه شخصی شما را (ازجمله ایمیل‌ها، برنامه‌‌ها و وب‌سایت‌ها) کنترل کند."</string>
+    <string name="monitoring_description_app_work" msgid="4612997849787922906">"نمایه کاری شما توسط <xliff:g id="ORGANIZATION">%1$s</xliff:g> مدیریت می‌شود. این نمایه به <xliff:g id="APPLICATION">%2$s</xliff:g> متصل است که می‌تواند فعالیت شما در شبکه (ازجمله ایمیل‌ها، برنامه‌ها و وب‌سایت‌ها) را پایش کند.\n\nبرای اطلاعات بیشتر، با سرپرست سیستم تماس بگیرید."</string>
     <string name="monitoring_description_app_personal_work" msgid="5664165460056859391">"نمایه کاری‌تان توسط <xliff:g id="ORGANIZATION">%1$s</xliff:g> مدیریت می‌شود. این نمایه به <xliff:g id="APPLICATION_WORK">%2$s</xliff:g> متصل است که می‌تواند تنظیمات، دسترسی شرکتی، برنامه‌ها، داده‌های مرتبط با دستگاه و اطلاعات مکان دستگاه شما را پایش کند.\n\nشما همچنین به <xliff:g id="APPLICATION_PERSONAL">%3$s</xliff:g> متصل هستید که می‌تواند فعالیت خصوصی شما را در شبکه پایش کند."</string>
     <string name="keyguard_indication_trust_granted" msgid="4985003749105182372">"قفل برای <xliff:g id="USER_NAME">%1$s</xliff:g> باز شد"</string>
     <string name="keyguard_indication_trust_managed" msgid="8319646760022357585">"<xliff:g id="TRUST_AGENT">%1$s</xliff:g> درحال اجرا است"</string>
@@ -681,7 +681,7 @@
     <string name="keyboard_shortcut_group_applications_assist" msgid="9095441910537146013">"دستیار"</string>
     <string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"مرورگر"</string>
     <string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"مخاطبین"</string>
-    <string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"رایانامه"</string>
+    <string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"ایمیل"</string>
     <string name="keyboard_shortcut_group_applications_sms" msgid="638701213803242744">"پیامک"</string>
     <string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"موسیقی"</string>
     <string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index aecf494..19492a0 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -354,7 +354,7 @@
         <item>com.android.systemui.LatencyTester</item>
         <item>com.android.systemui.globalactions.GlobalActionsComponent</item>
         <item>com.android.systemui.ScreenDecorations</item>
-        <item>com.android.systemui.fingerprint.FingerprintDialogImpl</item>
+        <item>com.android.systemui.biometrics.BiometricDialogImpl</item>
         <item>com.android.systemui.SliceBroadcastRelayHandler</item>
     </string-array>
 
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
index dce72b4..0ded963 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
@@ -394,6 +394,22 @@
     }
 
     /**
+     * Removes all the recent tasks.
+     */
+    public void removeAllRecentTasks() {
+        mBackgroundExecutor.submit(new Runnable() {
+            @Override
+            public void run() {
+                try {
+                    ActivityTaskManager.getService().removeAllVisibleRecentTasks();
+                } catch (RemoteException e) {
+                    Log.w(TAG, "Failed to remove all tasks", e);
+                }
+            }
+        });
+    }
+
+    /**
      * Cancels the current window transtion to/from Recents for the given task id.
      */
     public void cancelWindowTransition(int taskId) {
diff --git a/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java
index 4bb4c24..3c44eb4 100644
--- a/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java
@@ -340,7 +340,8 @@
         boolean bound = false;
         try {
             bound = mContext.bindServiceAsUser(launcherServiceIntent,
-                    mOverviewServiceConnection, Context.BIND_AUTO_CREATE,
+                    mOverviewServiceConnection,
+                    Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE,
                     UserHandle.of(mDeviceProvisionedController.getCurrentUser()));
         } catch (SecurityException e) {
             Log.e(TAG_OPS, "Unable to bind because of security error", e);
diff --git a/packages/SystemUI/src/com/android/systemui/fingerprint/FingerprintDialogImpl.java b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogImpl.java
similarity index 63%
rename from packages/SystemUI/src/com/android/systemui/fingerprint/FingerprintDialogImpl.java
rename to packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogImpl.java
index a81043e..6e62b0d 100644
--- a/packages/SystemUI/src/com/android/systemui/fingerprint/FingerprintDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogImpl.java
@@ -14,7 +14,7 @@
  * limitations under the License
  */
 
-package com.android.systemui.fingerprint;
+package com.android.systemui.biometrics;
 
 import android.content.Context;
 import android.content.pm.PackageManager;
@@ -31,25 +31,28 @@
 import com.android.systemui.SystemUI;
 import com.android.systemui.statusbar.CommandQueue;
 
-public class FingerprintDialogImpl extends SystemUI implements CommandQueue.Callbacks {
+/**
+ * Receives messages sent from AuthenticationClient and shows the appropriate biometric UI (e.g.
+ * FingerprintDialogView).
+ */
+public class BiometricDialogImpl extends SystemUI implements CommandQueue.Callbacks {
     private static final String TAG = "FingerprintDialogImpl";
     private static final boolean DEBUG = true;
 
-    protected static final int MSG_SHOW_DIALOG = 1;
-    protected static final int MSG_FINGERPRINT_AUTHENTICATED = 2;
-    protected static final int MSG_FINGERPRINT_HELP = 3;
-    protected static final int MSG_FINGERPRINT_ERROR = 4;
-    protected static final int MSG_HIDE_DIALOG = 5;
-    protected static final int MSG_BUTTON_NEGATIVE = 6;
-    protected static final int MSG_USER_CANCELED = 7;
-    protected static final int MSG_BUTTON_POSITIVE = 8;
-    protected static final int MSG_CLEAR_MESSAGE = 9;
-
+    private static final int MSG_SHOW_DIALOG = 1;
+    private static final int MSG_BIOMETRIC_AUTHENTICATED = 2;
+    private static final int MSG_BIOMETRIC_HELP = 3;
+    private static final int MSG_BIOMETRIC_ERROR = 4;
+    private static final int MSG_HIDE_DIALOG = 5;
+    private static final int MSG_BUTTON_NEGATIVE = 6;
+    private static final int MSG_USER_CANCELED = 7;
+    private static final int MSG_BUTTON_POSITIVE = 8;
 
     private FingerprintDialogView mDialogView;
     private WindowManager mWindowManager;
     private IBiometricPromptReceiver mReceiver;
     private boolean mDialogShowing;
+    private Callback mCallback = new Callback();
 
     private Handler mHandler = new Handler() {
         @Override
@@ -58,14 +61,14 @@
                 case MSG_SHOW_DIALOG:
                     handleShowDialog((SomeArgs) msg.obj);
                     break;
-                case MSG_FINGERPRINT_AUTHENTICATED:
-                    handleFingerprintAuthenticated();
+                case MSG_BIOMETRIC_AUTHENTICATED:
+                    handleBiometricAuthenticated();
                     break;
-                case MSG_FINGERPRINT_HELP:
-                    handleFingerprintHelp((String) msg.obj);
+                case MSG_BIOMETRIC_HELP:
+                    handleBiometricHelp((String) msg.obj);
                     break;
-                case MSG_FINGERPRINT_ERROR:
-                    handleFingerprintError((String) msg.obj);
+                case MSG_BIOMETRIC_ERROR:
+                    handleBiometricError((String) msg.obj);
                     break;
                 case MSG_HIDE_DIALOG:
                     handleHideDialog((Boolean) msg.obj);
@@ -79,13 +82,33 @@
                 case MSG_BUTTON_POSITIVE:
                     handleButtonPositive();
                     break;
-                case MSG_CLEAR_MESSAGE:
-                    handleClearMessage();
-                    break;
             }
         }
     };
 
+    private class Callback implements DialogViewCallback {
+        @Override
+        public void onUserCanceled() {
+            mHandler.obtainMessage(BiometricDialogImpl.MSG_USER_CANCELED).sendToTarget();
+        }
+
+        @Override
+        public void onErrorShown() {
+            mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_HIDE_DIALOG,
+                    false /* userCanceled */), BiometricPrompt.HIDE_DIALOG_DELAY);
+        }
+
+        @Override
+        public void onNegativePressed() {
+            mHandler.obtainMessage(BiometricDialogImpl.MSG_BUTTON_NEGATIVE).sendToTarget();
+        }
+
+        @Override
+        public void onPositivePressed() {
+            mHandler.obtainMessage(BiometricDialogImpl.MSG_BUTTON_POSITIVE).sendToTarget();
+        }
+    }
+
     @Override
     public void start() {
         if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) {
@@ -93,16 +116,16 @@
         }
         getComponent(CommandQueue.class).addCallbacks(this);
         mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
-        mDialogView = new FingerprintDialogView(mContext, mHandler);
+        mDialogView = new FingerprintDialogView(mContext, mCallback);
     }
 
     @Override
-    public void showFingerprintDialog(Bundle bundle, IBiometricPromptReceiver receiver) {
-        if (DEBUG) Log.d(TAG, "showFingerprintDialog");
+    public void showBiometricDialog(Bundle bundle, IBiometricPromptReceiver receiver) {
+        if (DEBUG) Log.d(TAG, "showBiometricDialog");
         // Remove these messages as they are part of the previous client
-        mHandler.removeMessages(MSG_FINGERPRINT_ERROR);
-        mHandler.removeMessages(MSG_FINGERPRINT_HELP);
-        mHandler.removeMessages(MSG_FINGERPRINT_AUTHENTICATED);
+        mHandler.removeMessages(MSG_BIOMETRIC_ERROR);
+        mHandler.removeMessages(MSG_BIOMETRIC_HELP);
+        mHandler.removeMessages(MSG_BIOMETRIC_AUTHENTICATED);
         SomeArgs args = SomeArgs.obtain();
         args.arg1 = bundle;
         args.arg2 = receiver;
@@ -110,26 +133,26 @@
     }
 
     @Override
-    public void onFingerprintAuthenticated() {
-        if (DEBUG) Log.d(TAG, "onFingerprintAuthenticated");
-        mHandler.obtainMessage(MSG_FINGERPRINT_AUTHENTICATED).sendToTarget();
+    public void onBiometricAuthenticated() {
+        if (DEBUG) Log.d(TAG, "onBiometricAuthenticated");
+        mHandler.obtainMessage(MSG_BIOMETRIC_AUTHENTICATED).sendToTarget();
     }
 
     @Override
-    public void onFingerprintHelp(String message) {
-        if (DEBUG) Log.d(TAG, "onFingerprintHelp: " + message);
-        mHandler.obtainMessage(MSG_FINGERPRINT_HELP, message).sendToTarget();
+    public void onBiometricHelp(String message) {
+        if (DEBUG) Log.d(TAG, "onBiometricHelp: " + message);
+        mHandler.obtainMessage(MSG_BIOMETRIC_HELP, message).sendToTarget();
     }
 
     @Override
-    public void onFingerprintError(String error) {
-        if (DEBUG) Log.d(TAG, "onFingerprintError: " + error);
-        mHandler.obtainMessage(MSG_FINGERPRINT_ERROR, error).sendToTarget();
+    public void onBiometricError(String error) {
+        if (DEBUG) Log.d(TAG, "onBiometricError: " + error);
+        mHandler.obtainMessage(MSG_BIOMETRIC_ERROR, error).sendToTarget();
     }
 
     @Override
-    public void hideFingerprintDialog() {
-        if (DEBUG) Log.d(TAG, "hideFingerprintDialog");
+    public void hideBiometricDialog() {
+        if (DEBUG) Log.d(TAG, "hideBiometricDialog");
         mHandler.obtainMessage(MSG_HIDE_DIALOG, false /* userCanceled */).sendToTarget();
     }
 
@@ -148,21 +171,23 @@
         mDialogShowing = true;
     }
 
-    private void handleFingerprintAuthenticated() {
-        if (DEBUG) Log.d(TAG, "handleFingerprintAuthenticated");
+    private void handleBiometricAuthenticated() {
+        if (DEBUG) Log.d(TAG, "handleBiometricAuthenticated");
+
+        // TODO: announce correct string depending on modality
         mDialogView.announceForAccessibility(
                 mContext.getResources().getText(
                         com.android.internal.R.string.fingerprint_authenticated));
         handleHideDialog(false /* userCanceled */);
     }
 
-    private void handleFingerprintHelp(String message) {
-        if (DEBUG) Log.d(TAG, "handleFingerprintHelp: " + message);
+    private void handleBiometricHelp(String message) {
+        if (DEBUG) Log.d(TAG, "handleBiometricHelp: " + message);
         mDialogView.showHelpMessage(message);
     }
 
-    private void handleFingerprintError(String error) {
-        if (DEBUG) Log.d(TAG, "handleFingerprintError: " + error);
+    private void handleBiometricError(String error) {
+        if (DEBUG) Log.d(TAG, "handleBiometricError: " + error);
         if (!mDialogShowing) {
             if (DEBUG) Log.d(TAG, "Dialog already dismissed");
             return;
@@ -216,10 +241,6 @@
         handleHideDialog(false /* userCanceled */);
     }
 
-    private void handleClearMessage() {
-        mDialogView.resetMessage();
-    }
-
     private void handleUserCanceled() {
         handleHideDialog(true /* userCanceled */);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/DialogViewCallback.java b/packages/SystemUI/src/com/android/systemui/biometrics/DialogViewCallback.java
new file mode 100644
index 0000000..f388d9c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/DialogViewCallback.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2018 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.systemui.biometrics;
+
+/**
+ * Callback interface for dialog views. These should be implemented by the controller (e.g.
+ * FingerprintDialogImpl) and passed into their views (e.g. FingerprintDialogView).
+ */
+public interface DialogViewCallback {
+    /**
+     * Invoked when the user cancels authentication by tapping outside the prompt, etc. The dialog
+     * should be dismissed.
+     */
+    void onUserCanceled();
+
+    /**
+     * Invoked when an error is shown. The dialog should be dismissed after a set amount of time.
+     */
+    void onErrorShown();
+
+    /**
+     * Invoked when the negative button is pressed. The client should be notified and the dialog
+     * should be dismissed.
+     */
+    void onNegativePressed();
+
+    /**
+     * Invoked when the positive button is pressed. The client should be notified and the dialog
+     * should be dismissed.
+     */
+    void onPositivePressed();
+}
diff --git a/packages/SystemUI/src/com/android/systemui/fingerprint/FingerprintDialogView.java b/packages/SystemUI/src/com/android/systemui/biometrics/FingerprintDialogView.java
similarity index 92%
rename from packages/SystemUI/src/com/android/systemui/fingerprint/FingerprintDialogView.java
rename to packages/SystemUI/src/com/android/systemui/biometrics/FingerprintDialogView.java
index 8013a9e..68c2c42 100644
--- a/packages/SystemUI/src/com/android/systemui/fingerprint/FingerprintDialogView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/FingerprintDialogView.java
@@ -14,7 +14,7 @@
  * limitations under the License
  */
 
-package com.android.systemui.fingerprint;
+package com.android.systemui.biometrics;
 
 import android.content.Context;
 import android.graphics.Color;
@@ -26,6 +26,7 @@
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
+import android.os.Message;
 import android.text.TextUtils;
 import android.util.DisplayMetrics;
 import android.util.Log;
@@ -56,6 +57,8 @@
     private static final int ANIMATION_DURATION_SHOW = 250; // ms
     private static final int ANIMATION_DURATION_AWAY = 350; // ms
 
+    private static final int MSG_CLEAR_MESSAGE = 1;
+
     private static final int STATE_NONE = 0;
     private static final int STATE_FINGERPRINT = 1;
     private static final int STATE_FINGERPRINT_ERROR = 2;
@@ -68,18 +71,17 @@
     private final int mErrorColor;
     private final int mTextColor;
     private final int mFingerprintColor;
+    private final float mDisplayWidth;
+    private final DialogViewCallback mCallback;
 
     private ViewGroup mLayout;
     private final TextView mErrorText;
-    private Handler mHandler;
     private Bundle mBundle;
     private final LinearLayout mDialog;
     private int mLastState;
     private boolean mAnimatingAway;
     private boolean mWasForceRemoved;
 
-    private final float mDisplayWidth;
-
     private final Runnable mShowAnimationRunnable = new Runnable() {
         @Override
         public void run() {
@@ -98,9 +100,23 @@
         }
     };
 
-    public FingerprintDialogView(Context context, Handler handler) {
+    private Handler mHandler = new Handler() {
+        @Override
+        public void handleMessage(Message msg) {
+            switch(msg.what) {
+                case MSG_CLEAR_MESSAGE:
+                    handleClearMessage();
+                    break;
+                default:
+                    Log.e(TAG, "Unhandled message: " + msg.what);
+                    break;
+            }
+        }
+    };
+
+    public FingerprintDialogView(Context context, DialogViewCallback callback) {
         super(context);
-        mHandler = handler;
+        mCallback = callback;
         mLinearOutSlowIn = Interpolators.LINEAR_OUT_SLOW_IN;
         mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
         mAnimationTranslationOffset = getResources()
@@ -138,7 +154,7 @@
                     downPressed = false;
                 } else if (event.getAction() == KeyEvent.ACTION_UP && downPressed == true) {
                     downPressed = false;
-                    mHandler.obtainMessage(FingerprintDialogImpl.MSG_USER_CANCELED).sendToTarget();
+                    mCallback.onUserCanceled();
                 }
                 return true;
             }
@@ -155,11 +171,11 @@
         setDismissesDialog(rightSpace);
 
         negative.setOnClickListener((View v) -> {
-            mHandler.obtainMessage(FingerprintDialogImpl.MSG_BUTTON_NEGATIVE).sendToTarget();
+            mCallback.onNegativePressed();
         });
 
         positive.setOnClickListener((View v) -> {
-            mHandler.obtainMessage(FingerprintDialogImpl.MSG_BUTTON_POSITIVE).sendToTarget();
+            mCallback.onPositivePressed();
         });
 
         mLayout.setFocusableInTouchMode(true);
@@ -230,8 +246,7 @@
     private void setDismissesDialog(View v) {
         v.setClickable(true);
         v.setOnTouchListener((View view, MotionEvent event) -> {
-            mHandler.obtainMessage(FingerprintDialogImpl.MSG_HIDE_DIALOG, true /* userCanceled */)
-                    .sendToTarget();
+            mCallback.onUserCanceled();
             return true;
         });
     }
@@ -289,7 +304,7 @@
     }
 
     // Clears the temporary message and shows the help message.
-    protected void resetMessage() {
+    private void handleClearMessage() {
         updateFingerprintIcon(STATE_FINGERPRINT);
         mErrorText.setText(R.string.fingerprint_dialog_touch_sensor);
         mErrorText.setTextColor(mTextColor);
@@ -297,12 +312,12 @@
 
     // Shows an error/help message
     private void showTemporaryMessage(String message) {
-        mHandler.removeMessages(FingerprintDialogImpl.MSG_CLEAR_MESSAGE);
+        mHandler.removeMessages(MSG_CLEAR_MESSAGE);
         updateFingerprintIcon(STATE_FINGERPRINT_ERROR);
         mErrorText.setText(message);
         mErrorText.setTextColor(mErrorColor);
         mErrorText.setContentDescription(message);
-        mHandler.sendMessageDelayed(mHandler.obtainMessage(FingerprintDialogImpl.MSG_CLEAR_MESSAGE),
+        mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_CLEAR_MESSAGE),
                 BiometricPrompt.HIDE_DIALOG_DELAY);
     }
 
@@ -312,8 +327,7 @@
 
     public void showErrorMessage(String error) {
         showTemporaryMessage(error);
-        mHandler.sendMessageDelayed(mHandler.obtainMessage(FingerprintDialogImpl.MSG_HIDE_DIALOG,
-                false /* userCanceled */), BiometricPrompt.HIDE_DIALOG_DELAY);
+        mCallback.onErrorShown();
     }
 
     private void updateFingerprintIcon(int newState) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
index 91f1d3d..f13f489 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
@@ -55,6 +55,7 @@
     private Scroller mScroller;
 
     private AnimatorSet mBounceAnimatorSet;
+    private int mAnimatingToPage = -1;
     private float mLastExpansion;
 
     public PagedTileLayout(Context context, AttributeSet attrs) {
@@ -94,16 +95,40 @@
     }
 
     @Override
+    public boolean onInterceptTouchEvent(MotionEvent ev) {
+        // Suppress all touch event during reveal animation.
+        if (mAnimatingToPage != -1) {
+            return true;
+        }
+        return super.onInterceptTouchEvent(ev);
+    }
+
+    @Override
+    public boolean onTouchEvent(MotionEvent ev) {
+        // Suppress all touch event during reveal animation.
+        if (mAnimatingToPage != -1) {
+            return true;
+        }
+        return super.onTouchEvent(ev);
+    }
+
+    @Override
     public void computeScroll() {
         if (!mScroller.isFinished() && mScroller.computeScrollOffset()) {
-            fakeDragBy(getScrollX() - mScroller.getCurrX());
+            scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
+            float pageFraction = (float) getScrollX() / getWidth();
+            int position = (int) pageFraction;
+            float positionOffset = pageFraction - position;
+            mOnPageChangeListener.onPageScrolled(position, positionOffset, getScrollX());
             // Keep on drawing until the animation has finished.
             postInvalidateOnAnimation();
             return;
-        } else if (isFakeDragging()) {
-            endFakeDrag();
+        }
+        if (mAnimatingToPage != -1) {
+            setCurrentItem(mAnimatingToPage, true);
             mBounceAnimatorSet.start();
             setOffscreenPageLimit(1);
+            mAnimatingToPage = -1;
         }
         super.computeScroll();
     }
@@ -262,7 +287,7 @@
     }
 
     public void startTileReveal(Set<String> tileSpecs, final Runnable postAnimation) {
-        if (tileSpecs.isEmpty() || mPages.size() < 2 || getScrollX() != 0 || !beginFakeDrag()) {
+        if (tileSpecs.isEmpty() || mPages.size() < 2 || getScrollX() != 0) {
             // Do not start the reveal animation unless there are tiles to animate, multiple
             // TilePages available and the user has not already started dragging.
             return;
@@ -292,8 +317,9 @@
                 postAnimation.run();
             }
         });
-        setOffscreenPageLimit(lastPageNumber); // Ensure the page to reveal has been inflated.
-        mScroller.startScroll(getScrollX(), getScrollY(), getWidth() * lastPageNumber, 0,
+        mAnimatingToPage = lastPageNumber;
+        setOffscreenPageLimit(mAnimatingToPage); // Ensure the page to reveal has been inflated.
+        mScroller.startScroll(getScrollX(), getScrollY(), getWidth() * mAnimatingToPage, 0,
                 REVEAL_SCROLL_DURATION_MILLIS);
         postInvalidateOnAnimation();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index 145a246..909cd79 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -85,11 +85,11 @@
     private static final int MSG_SHOW_SHUTDOWN_UI              = 36 << MSG_SHIFT;
     private static final int MSG_SET_TOP_APP_HIDES_STATUS_BAR  = 37 << MSG_SHIFT;
     private static final int MSG_ROTATION_PROPOSAL             = 38 << MSG_SHIFT;
-    private static final int MSG_FINGERPRINT_SHOW              = 39 << MSG_SHIFT;
-    private static final int MSG_FINGERPRINT_AUTHENTICATED     = 40 << MSG_SHIFT;
-    private static final int MSG_FINGERPRINT_HELP              = 41 << MSG_SHIFT;
-    private static final int MSG_FINGERPRINT_ERROR             = 42 << MSG_SHIFT;
-    private static final int MSG_FINGERPRINT_HIDE              = 43 << MSG_SHIFT;
+    private static final int MSG_BIOMETRIC_SHOW                = 39 << MSG_SHIFT;
+    private static final int MSG_BIOMETRIC_AUTHENTICATED       = 40 << MSG_SHIFT;
+    private static final int MSG_BIOMETRIC_HELP                = 41 << MSG_SHIFT;
+    private static final int MSG_BIOMETRIC_ERROR               = 42 << MSG_SHIFT;
+    private static final int MSG_BIOMETRIC_HIDE                = 43 << MSG_SHIFT;
     private static final int MSG_SHOW_CHARGING_ANIMATION       = 44 << MSG_SHIFT;
     private static final int MSG_SHOW_PINNING_TOAST_ENTER_EXIT = 45 << MSG_SHIFT;
     private static final int MSG_SHOW_PINNING_TOAST_ESCAPE     = 46 << MSG_SHIFT;
@@ -160,11 +160,11 @@
 
         default void onRotationProposal(int rotation, boolean isValid) { }
 
-        default void showFingerprintDialog(Bundle bundle, IBiometricPromptReceiver receiver) { }
-        default void onFingerprintAuthenticated() { }
-        default void onFingerprintHelp(String message) { }
-        default void onFingerprintError(String error) { }
-        default void hideFingerprintDialog() { }
+        default void showBiometricDialog(Bundle bundle, IBiometricPromptReceiver receiver) { }
+        default void onBiometricAuthenticated() { }
+        default void onBiometricHelp(String message) { }
+        default void onBiometricError(String error) { }
+        default void hideBiometricDialog() { }
     }
 
     @VisibleForTesting
@@ -513,41 +513,41 @@
     }
 
     @Override
-    public void showFingerprintDialog(Bundle bundle, IBiometricPromptReceiver receiver) {
+    public void showBiometricDialog(Bundle bundle, IBiometricPromptReceiver receiver) {
         synchronized (mLock) {
             SomeArgs args = SomeArgs.obtain();
             args.arg1 = bundle;
             args.arg2 = receiver;
-            mHandler.obtainMessage(MSG_FINGERPRINT_SHOW, args)
+            mHandler.obtainMessage(MSG_BIOMETRIC_SHOW, args)
                     .sendToTarget();
         }
     }
 
     @Override
-    public void onFingerprintAuthenticated() {
+    public void onBiometricAuthenticated() {
         synchronized (mLock) {
-            mHandler.obtainMessage(MSG_FINGERPRINT_AUTHENTICATED).sendToTarget();
+            mHandler.obtainMessage(MSG_BIOMETRIC_AUTHENTICATED).sendToTarget();
         }
     }
 
     @Override
-    public void onFingerprintHelp(String message) {
+    public void onBiometricHelp(String message) {
         synchronized (mLock) {
-            mHandler.obtainMessage(MSG_FINGERPRINT_HELP, message).sendToTarget();
+            mHandler.obtainMessage(MSG_BIOMETRIC_HELP, message).sendToTarget();
         }
     }
 
     @Override
-    public void onFingerprintError(String error) {
+    public void onBiometricError(String error) {
         synchronized (mLock) {
-            mHandler.obtainMessage(MSG_FINGERPRINT_ERROR, error).sendToTarget();
+            mHandler.obtainMessage(MSG_BIOMETRIC_ERROR, error).sendToTarget();
         }
     }
 
     @Override
-    public void hideFingerprintDialog() {
+    public void hideBiometricDialog() {
         synchronized (mLock) {
-            mHandler.obtainMessage(MSG_FINGERPRINT_HIDE).sendToTarget();
+            mHandler.obtainMessage(MSG_BIOMETRIC_HIDE).sendToTarget();
         }
     }
 
@@ -752,34 +752,34 @@
                         mCallbacks.get(i).onRotationProposal(msg.arg1, msg.arg2 != 0);
                     }
                     break;
-                case MSG_FINGERPRINT_SHOW:
-                    mHandler.removeMessages(MSG_FINGERPRINT_ERROR);
-                    mHandler.removeMessages(MSG_FINGERPRINT_HELP);
-                    mHandler.removeMessages(MSG_FINGERPRINT_AUTHENTICATED);
+                case MSG_BIOMETRIC_SHOW:
+                    mHandler.removeMessages(MSG_BIOMETRIC_ERROR);
+                    mHandler.removeMessages(MSG_BIOMETRIC_HELP);
+                    mHandler.removeMessages(MSG_BIOMETRIC_AUTHENTICATED);
                     for (int i = 0; i < mCallbacks.size(); i++) {
-                        mCallbacks.get(i).showFingerprintDialog(
+                        mCallbacks.get(i).showBiometricDialog(
                                 (Bundle)((SomeArgs)msg.obj).arg1,
                                 (IBiometricPromptReceiver)((SomeArgs)msg.obj).arg2);
                     }
                     break;
-                case MSG_FINGERPRINT_AUTHENTICATED:
+                case MSG_BIOMETRIC_AUTHENTICATED:
                     for (int i = 0; i < mCallbacks.size(); i++) {
-                        mCallbacks.get(i).onFingerprintAuthenticated();
+                        mCallbacks.get(i).onBiometricAuthenticated();
                     }
                     break;
-                case MSG_FINGERPRINT_HELP:
+                case MSG_BIOMETRIC_HELP:
                     for (int i = 0; i < mCallbacks.size(); i++) {
-                        mCallbacks.get(i).onFingerprintHelp((String) msg.obj);
+                        mCallbacks.get(i).onBiometricHelp((String) msg.obj);
                     }
                     break;
-                case MSG_FINGERPRINT_ERROR:
+                case MSG_BIOMETRIC_ERROR:
                     for (int i = 0; i < mCallbacks.size(); i++) {
-                        mCallbacks.get(i).onFingerprintError((String) msg.obj);
+                        mCallbacks.get(i).onBiometricError((String) msg.obj);
                     }
                     break;
-                case MSG_FINGERPRINT_HIDE:
+                case MSG_BIOMETRIC_HIDE:
                     for (int i = 0; i < mCallbacks.size(); i++) {
-                        mCallbacks.get(i).hideFingerprintDialog();
+                        mCallbacks.get(i).hideBiometricDialog();
                     }
                     break;
                 case MSG_SHOW_CHARGING_ANIMATION:
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationUiAdjustment.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationUiAdjustment.java
index 92bf821..47b7fe9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationUiAdjustment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationUiAdjustment.java
@@ -27,6 +27,7 @@
 import androidx.annotation.VisibleForTesting;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
 import java.util.Objects;
@@ -38,18 +39,21 @@
 
     public final String key;
     public final List<Notification.Action> smartActions;
+    public final CharSequence[] smartReplies;
 
     @VisibleForTesting
-    NotificationUiAdjustment(String key, List<Notification.Action> smartActions) {
+    NotificationUiAdjustment(
+            String key, List<Notification.Action> smartActions, CharSequence[] smartReplies) {
         this.key = key;
         this.smartActions = smartActions == null
                 ? Collections.emptyList()
                 : new ArrayList<>(smartActions);
+        this.smartReplies = smartReplies == null ? new CharSequence[0] : smartReplies.clone();
     }
 
     public static NotificationUiAdjustment extractFromNotificationEntry(
             NotificationData.Entry entry) {
-        return new NotificationUiAdjustment(entry.key, entry.smartActions);
+        return new NotificationUiAdjustment(entry.key, entry.smartActions, entry.smartReplies);
     }
 
     public static boolean needReinflate(
@@ -58,7 +62,13 @@
         if (oldAdjustment == newAdjustment) {
             return false;
         }
-        return areDifferent(oldAdjustment.smartActions, newAdjustment.smartActions);
+        if (areDifferent(oldAdjustment.smartActions, newAdjustment.smartActions)) {
+            return true;
+        }
+        if (!Arrays.equals(oldAdjustment.smartReplies, newAdjustment.smartReplies)) {
+            return true;
+        }
+        return false;
     }
 
     public static boolean areDifferent(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java
index ce8f224..804e842 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationData.java
@@ -112,6 +112,7 @@
         public int userSentiment = Ranking.USER_SENTIMENT_NEUTRAL;
         @NonNull
         public List<Notification.Action> smartActions = Collections.emptyList();
+        public CharSequence[] smartReplies = new CharSequence[0];
 
         private int mCachedContrastColor = COLOR_INVALID;
         private int mCachedContrastColorIsFor = COLOR_INVALID;
@@ -155,6 +156,9 @@
             userSentiment = ranking.getUserSentiment();
             smartActions = ranking.getSmartActions() == null
                     ? Collections.emptyList() : ranking.getSmartActions();
+            smartReplies = ranking.getSmartReplies() == null
+                    ? new CharSequence[0]
+                    : ranking.getSmartReplies().toArray(new CharSequence[0]);
         }
 
         public void setInterruption() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
index da1fd3b..0110610 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
@@ -36,6 +36,7 @@
 import android.widget.LinearLayout;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.ContrastColorUtil;
 import com.android.systemui.Dependency;
 import com.android.systemui.R;
@@ -1229,32 +1230,40 @@
         boolean hasRemoteInput = false;
         RemoteInput remoteInputWithChoices = null;
         PendingIntent pendingIntentWithChoices = null;
+        CharSequence[] choices = null;
 
         Notification.Action[] actions = entry.notification.getNotification().actions;
         if (actions != null) {
             for (Notification.Action a : actions) {
-                if (a.getRemoteInputs() != null) {
-                    for (RemoteInput ri : a.getRemoteInputs()) {
-                        boolean showRemoteInputView = ri.getAllowFreeFormInput();
-                        boolean showSmartReplyView = enableSmartReplies && ri.getChoices() != null
-                                && ri.getChoices().length > 0;
-                        if (showRemoteInputView) {
-                            hasRemoteInput = true;
+                if (a.getRemoteInputs() == null) {
+                    continue;
+                }
+                for (RemoteInput ri : a.getRemoteInputs()) {
+                    boolean showRemoteInputView = ri.getAllowFreeFormInput();
+                    boolean showSmartReplyView = enableSmartReplies
+                            && (ArrayUtils.isEmpty(ri.getChoices())
+                            || (showRemoteInputView && !ArrayUtils.isEmpty(entry.smartReplies)));
+                    if (showRemoteInputView) {
+                        hasRemoteInput = true;
+                    }
+                    if (showSmartReplyView) {
+                        remoteInputWithChoices = ri;
+                        pendingIntentWithChoices = a.actionIntent;
+                        if (!ArrayUtils.isEmpty(ri.getChoices())) {
+                            choices = ri.getChoices();
+                        } else {
+                            choices = entry.smartReplies;
                         }
-                        if (showSmartReplyView) {
-                            remoteInputWithChoices = ri;
-                            pendingIntentWithChoices = a.actionIntent;
-                        }
-                        if (showRemoteInputView || showSmartReplyView) {
-                            break;
-                        }
+                    }
+                    if (showRemoteInputView || showSmartReplyView) {
+                        break;
                     }
                 }
             }
         }
 
         applyRemoteInput(entry, hasRemoteInput);
-        applySmartReplyView(remoteInputWithChoices, pendingIntentWithChoices, entry);
+        applySmartReplyView(remoteInputWithChoices, pendingIntentWithChoices, entry, choices);
     }
 
     private void applyRemoteInput(NotificationData.Entry entry, boolean hasRemoteInput) {
@@ -1356,10 +1365,10 @@
     }
 
     private void applySmartReplyView(RemoteInput remoteInput, PendingIntent pendingIntent,
-            NotificationData.Entry entry) {
+            NotificationData.Entry entry, CharSequence[] choices) {
         if (mExpandedChild != null) {
             mExpandedSmartReplyView =
-                    applySmartReplyView(mExpandedChild, remoteInput, pendingIntent, entry);
+                    applySmartReplyView(mExpandedChild, remoteInput, pendingIntent, entry, choices);
             if (mExpandedSmartReplyView != null && remoteInput != null
                     && remoteInput.getChoices() != null && remoteInput.getChoices().length > 0) {
                 mSmartReplyController.smartRepliesAdded(entry, remoteInput.getChoices().length);
@@ -1369,7 +1378,7 @@
 
     private SmartReplyView applySmartReplyView(
             View view, RemoteInput remoteInput, PendingIntent pendingIntent,
-            NotificationData.Entry entry) {
+            NotificationData.Entry entry, CharSequence[] choices) {
         View smartReplyContainerCandidate = view.findViewById(
                 com.android.internal.R.id.smart_reply_container);
         if (!(smartReplyContainerCandidate instanceof LinearLayout)) {
@@ -1406,7 +1415,8 @@
         }
         if (smartReplyView != null) {
             smartReplyView.setRepliesFromRemoteInput(remoteInput, pendingIntent,
-                    mSmartReplyController, entry, smartReplyContainer);
+                    mSmartReplyController, entry, smartReplyContainer, choices
+            );
             smartReplyContainer.setVisibility(View.VISIBLE);
         }
         return smartReplyView;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
index 252ab22..c76a4b5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
@@ -173,14 +173,14 @@
                 Math.max(getChildCount(), 1), DECREASING_MEASURED_WIDTH_WITHOUT_PADDING_COMPARATOR);
     }
 
-    public void setRepliesFromRemoteInput(RemoteInput remoteInput, PendingIntent pendingIntent,
+    public void setRepliesFromRemoteInput(
+            RemoteInput remoteInput, PendingIntent pendingIntent,
             SmartReplyController smartReplyController, NotificationData.Entry entry,
-            View smartReplyContainer) {
+            View smartReplyContainer, CharSequence[] choices) {
         mSmartReplyContainer = smartReplyContainer;
         removeAllViews();
         mCurrentBackgroundColor = mDefaultBackgroundColor;
         if (remoteInput != null && pendingIntent != null) {
-            CharSequence[] choices = remoteInput.getChoices();
             if (choices != null) {
                 for (int i = 0; i < choices.length; ++i) {
                     Button replyButton = inflateReplyButton(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationUiAdjustmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationUiAdjustmentTest.java
index ce47e60..db1e049 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationUiAdjustmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationUiAdjustmentTest.java
@@ -30,6 +30,7 @@
 import org.junit.Test;
 
 import java.util.Collections;
+import java.util.List;
 
 @SmallTest
 public class NotificationUiAdjustmentTest extends SysuiTestCase {
@@ -41,8 +42,8 @@
         Notification.Action action =
                 createActionBuilder("first", R.drawable.ic_corp_icon, pendingIntent).build();
         assertThat(NotificationUiAdjustment.needReinflate(
-                new NotificationUiAdjustment("first", Collections.emptyList()),
-                new NotificationUiAdjustment("second", Collections.singletonList(action))))
+                createUiAdjustmentFromSmartActions("first", Collections.emptyList()),
+                createUiAdjustmentFromSmartActions("second", Collections.singletonList(action))))
                 .isTrue();
     }
 
@@ -56,8 +57,8 @@
                 createActionBuilder("second", R.drawable.ic_corp_icon, pendingIntent).build();
 
         assertThat(NotificationUiAdjustment.needReinflate(
-                new NotificationUiAdjustment("first", Collections.singletonList(firstAction)),
-                new NotificationUiAdjustment("second", Collections.singletonList(secondAction))))
+                createUiAdjustmentFromSmartActions("first", Collections.singletonList(firstAction)),
+                createUiAdjustmentFromSmartActions("second", Collections.singletonList(secondAction))))
                 .isTrue();
     }
 
@@ -72,8 +73,8 @@
                         .build();
 
         assertThat(NotificationUiAdjustment.needReinflate(
-                new NotificationUiAdjustment("first", Collections.singletonList(firstAction)),
-                new NotificationUiAdjustment("second", Collections.singletonList(secondAction))))
+                createUiAdjustmentFromSmartActions("first", Collections.singletonList(firstAction)),
+                createUiAdjustmentFromSmartActions("second", Collections.singletonList(secondAction))))
                 .isTrue();
     }
 
@@ -91,8 +92,8 @@
                         .build();
 
         assertThat(NotificationUiAdjustment.needReinflate(
-                new NotificationUiAdjustment("first", Collections.singletonList(firstAction)),
-                new NotificationUiAdjustment("second", Collections.singletonList(secondAction))))
+                createUiAdjustmentFromSmartActions("first", Collections.singletonList(firstAction)),
+                createUiAdjustmentFromSmartActions("second", Collections.singletonList(secondAction))))
                 .isTrue();
     }
 
@@ -116,8 +117,8 @@
                         .build();
 
         assertThat(NotificationUiAdjustment.needReinflate(
-                new NotificationUiAdjustment("first", Collections.singletonList(firstAction)),
-                new NotificationUiAdjustment("second", Collections.singletonList(secondAction))))
+                createUiAdjustmentFromSmartActions("first", Collections.singletonList(firstAction)),
+                createUiAdjustmentFromSmartActions("second", Collections.singletonList(secondAction))))
                 .isTrue();
     }
 
@@ -141,8 +142,8 @@
                         .build();
 
         assertThat(NotificationUiAdjustment.needReinflate(
-                new NotificationUiAdjustment("first", Collections.singletonList(firstAction)),
-                new NotificationUiAdjustment("second", Collections.singletonList(secondAction))))
+                createUiAdjustmentFromSmartActions("first", Collections.singletonList(firstAction)),
+                createUiAdjustmentFromSmartActions("second", Collections.singletonList(secondAction))))
                 .isTrue();
     }
 
@@ -163,8 +164,25 @@
                         .addRemoteInput(secondRemoteInput).build();
 
         assertThat(NotificationUiAdjustment.needReinflate(
-                new NotificationUiAdjustment("first", Collections.singletonList(firstAction)),
-                new NotificationUiAdjustment("second", Collections.singletonList(secondAction))))
+                createUiAdjustmentFromSmartActions("first", Collections.singletonList(firstAction)),
+                createUiAdjustmentFromSmartActions(
+                        "second", Collections.singletonList(secondAction))))
+                .isFalse();
+    }
+
+    @Test
+    public void needReinflate_differentSmartReplies() {
+        assertThat(NotificationUiAdjustment.needReinflate(
+                createUiAdjustmentFromSmartReplies("first", new CharSequence[]{"a", "b"}),
+                createUiAdjustmentFromSmartReplies("first", new CharSequence[] {"b", "a"})))
+                .isTrue();
+    }
+
+    @Test
+    public void needReinflate_sameSmartReplies() {
+        assertThat(NotificationUiAdjustment.needReinflate(
+                createUiAdjustmentFromSmartReplies("first", new CharSequence[] {"a", "b"}),
+                createUiAdjustmentFromSmartReplies("first", new CharSequence[] {"a", "b"})))
                 .isFalse();
     }
 
@@ -177,4 +195,14 @@
     private RemoteInput createRemoteInput(String resultKey, String label, CharSequence[] choices) {
         return new RemoteInput.Builder(resultKey).setLabel(label).setChoices(choices).build();
     }
+
+    private NotificationUiAdjustment createUiAdjustmentFromSmartActions(
+            String key, List<Notification.Action> actions) {
+        return new NotificationUiAdjustment(key, actions, new CharSequence[0]);
+    }
+
+    private NotificationUiAdjustment createUiAdjustmentFromSmartReplies(
+            String key, CharSequence[] replies) {
+        return new NotificationUiAdjustment(key, Collections.emptyList(), replies);
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationDataTest.java
index c3683b3..de5a8a0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationDataTest.java
@@ -430,21 +430,22 @@
                         outRanking.getImportance(), outRanking.getImportanceExplanation(),
                         outRanking.getOverrideGroupKey(), outRanking.getChannel(), null, null,
                         outRanking.canShowBadge(), outRanking.getUserSentiment(), true,
-                        null);
+                        null, null);
             } else if (key.equals(TEST_EXEMPT_DND_VISUAL_SUPPRESSION_KEY)) {
                 outRanking.populate(key, outRanking.getRank(),
                         outRanking.matchesInterruptionFilter(),
                         outRanking.getVisibilityOverride(), 255,
                         outRanking.getImportance(), outRanking.getImportanceExplanation(),
                         outRanking.getOverrideGroupKey(), outRanking.getChannel(), null, null,
-                        outRanking.canShowBadge(), outRanking.getUserSentiment(), true, null);
+                        outRanking.canShowBadge(), outRanking.getUserSentiment(), true, null, null);
             } else {
                 outRanking.populate(key, outRanking.getRank(),
                         outRanking.matchesInterruptionFilter(),
                         outRanking.getVisibilityOverride(), outRanking.getSuppressedVisualEffects(),
                         outRanking.getImportance(), outRanking.getImportanceExplanation(),
                         outRanking.getOverrideGroupKey(), NOTIFICATION_CHANNEL, null, null,
-                        outRanking.canShowBadge(), outRanking.getUserSentiment(), false, null);
+                        outRanking.canShowBadge(), outRanking.getUserSentiment(), false, null,
+                        null);
             }
             return true;
         }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
index 61c1ecb..6543bdb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
@@ -157,7 +157,7 @@
                     0,
                     NotificationManager.IMPORTANCE_DEFAULT,
                     null, null,
-                    null, null, null, true, sentiment, false, null);
+                    null, null, null, true, sentiment, false, null, null);
             return true;
         }).when(mRankingMap).getRanking(eq(key), any(NotificationListenerService.Ranking.class));
     }
@@ -176,7 +176,7 @@
                     null, null,
                     null, null, null, true,
                     NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL, false,
-                    smartActions);
+                    smartActions, null);
             return true;
         }).when(mRankingMap).getRanking(eq(key), any(NotificationListenerService.Ranking.class));
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java
index 5ac2190..01e6307 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java
@@ -375,7 +375,7 @@
         PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, 0,
                 new Intent(TEST_ACTION), 0);
         RemoteInput input = new RemoteInput.Builder(TEST_RESULT_KEY).setChoices(choices).build();
-        mView.setRepliesFromRemoteInput(input, pendingIntent, mLogger, mEntry, mContainer);
+        mView.setRepliesFromRemoteInput(input, pendingIntent, mLogger, mEntry, mContainer, choices);
     }
 
     /** Builds a {@link ViewGroup} whose measures and layout mirror a {@link SmartReplyView}. */
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
index 11b2343..cf08681 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
@@ -22,10 +22,8 @@
 import android.util.DebugUtils;
 import android.util.ExceptionUtils;
 import android.util.Log;
-import android.util.Pools.SimplePool;
 import android.util.Slog;
 import android.util.SparseBooleanArray;
-import android.view.Choreographer;
 import android.view.InputDevice;
 import android.view.InputEvent;
 import android.view.InputFilter;
@@ -104,31 +102,12 @@
             | FLAG_FEATURE_AUTOCLICK | FLAG_FEATURE_TOUCH_EXPLORATION
             | FLAG_FEATURE_SCREEN_MAGNIFIER | FLAG_FEATURE_TRIGGERED_SCREEN_MAGNIFIER;
 
-    private final Runnable mProcessBatchedEventsRunnable = new Runnable() {
-        @Override
-        public void run() {
-            final long frameTimeNanos = mChoreographer.getFrameTimeNanos();
-            if (DEBUG) {
-                Slog.i(TAG, "Begin batch processing for frame: " + frameTimeNanos);
-            }
-            processBatchedEvents(frameTimeNanos);
-            if (DEBUG) {
-                Slog.i(TAG, "End batch processing.");
-            }
-            if (mEventQueue != null) {
-                scheduleProcessBatchedEvents();
-            }
-        }
-    };
-
     private final Context mContext;
 
     private final PowerManager mPm;
 
     private final AccessibilityManagerService mAms;
 
-    private final Choreographer mChoreographer;
-
     private boolean mInstalled;
 
     private int mUserId;
@@ -147,8 +126,6 @@
 
     private EventStreamTransformation mEventHandler;
 
-    private MotionEventHolder mEventQueue;
-
     private EventStreamState mMouseStreamState;
 
     private EventStreamState mTouchScreenStreamState;
@@ -160,7 +137,6 @@
         mContext = context;
         mAms = service;
         mPm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
-        mChoreographer = Choreographer.getInstance();
     }
 
     @Override
@@ -274,7 +250,7 @@
             return;
         }
 
-        batchMotionEvent(event, policyFlags);
+        handleMotionEvent(event, policyFlags);
     }
 
     private void processKeyEvent(EventStreamState state, KeyEvent event, int policyFlags) {
@@ -285,68 +261,14 @@
         mEventHandler.onKeyEvent(event, policyFlags);
     }
 
-    private void scheduleProcessBatchedEvents() {
-        mChoreographer.postCallback(Choreographer.CALLBACK_INPUT,
-                mProcessBatchedEventsRunnable, null);
-    }
-
-    private void batchMotionEvent(MotionEvent event, int policyFlags) {
-        if (DEBUG) {
-            Slog.i(TAG, "Batching event: " + event + ", policyFlags: " + policyFlags);
-        }
-        if (mEventQueue == null) {
-            mEventQueue = MotionEventHolder.obtain(event, policyFlags);
-            scheduleProcessBatchedEvents();
-            return;
-        }
-        if (mEventQueue.event.addBatch(event)) {
-            return;
-        }
-        MotionEventHolder holder = MotionEventHolder.obtain(event, policyFlags);
-        holder.next = mEventQueue;
-        mEventQueue.previous = holder;
-        mEventQueue = holder;
-    }
-
-    private void processBatchedEvents(long frameNanos) {
-        MotionEventHolder current = mEventQueue;
-        if (current == null) {
-            return;
-        }
-        while (current.next != null) {
-            current = current.next;
-        }
-        while (true) {
-            if (current == null) {
-                mEventQueue = null;
-                break;
-            }
-            if (current.event.getEventTimeNano() >= frameNanos) {
-                // Finished with this choreographer frame. Do the rest on the next one.
-                current.next = null;
-                break;
-            }
-            handleMotionEvent(current.event, current.policyFlags);
-            MotionEventHolder prior = current;
-            current = current.previous;
-            prior.recycle();
-        }
-    }
-
     private void handleMotionEvent(MotionEvent event, int policyFlags) {
         if (DEBUG) {
-            Slog.i(TAG, "Handling batched event: " + event + ", policyFlags: " + policyFlags);
+            Slog.i(TAG, "Handling motion event: " + event + ", policyFlags: " + policyFlags);
         }
-        // Since we do batch processing it is possible that by the time the
-        // next batch is processed the event handle had been set to null.
-        if (mEventHandler != null) {
-            mPm.userActivity(event.getEventTime(), false);
-            MotionEvent transformedEvent = MotionEvent.obtain(event);
-            mEventHandler.onMotionEvent(transformedEvent, event, policyFlags);
-            transformedEvent.recycle();
-        } else {
-            if (DEBUG) Slog.d(TAG, "mEventHandler == null for " + event);
-        }
+        mPm.userActivity(event.getEventTime(), false);
+        MotionEvent transformedEvent = MotionEvent.obtain(event);
+        mEventHandler.onMotionEvent(transformedEvent, event, policyFlags);
+        transformedEvent.recycle();
     }
 
     @Override
@@ -469,9 +391,6 @@
     }
 
     private void disableFeatures() {
-        // Give the features a chance to process any batched events so we'll keep a consistent
-        // event stream
-        processBatchedEvents(Long.MAX_VALUE);
         if (mMotionEventInjector != null) {
             mAms.setMotionEventInjector(null);
             mMotionEventInjector.onDestroy();
@@ -515,36 +434,6 @@
         /* ignore */
     }
 
-    private static class MotionEventHolder {
-        private static final int MAX_POOL_SIZE = 32;
-        private static final SimplePool<MotionEventHolder> sPool =
-                new SimplePool<MotionEventHolder>(MAX_POOL_SIZE);
-
-        public int policyFlags;
-        public MotionEvent event;
-        public MotionEventHolder next;
-        public MotionEventHolder previous;
-
-        public static MotionEventHolder obtain(MotionEvent event, int policyFlags) {
-            MotionEventHolder holder = sPool.acquire();
-            if (holder == null) {
-                holder = new MotionEventHolder();
-            }
-            holder.event = MotionEvent.obtain(event);
-            holder.policyFlags = policyFlags;
-            return holder;
-        }
-
-        public void recycle() {
-            event.recycle();
-            event = null;
-            policyFlags = 0;
-            next = null;
-            previous = null;
-            sPool.release(this);
-        }
-    }
-
     /**
      * Keeps state of event streams observed for an input device with a certain source.
      * Provides information about whether motion and key events should be processed by accessibility
diff --git a/services/core/java/com/android/server/BinderCallsStatsService.java b/services/core/java/com/android/server/BinderCallsStatsService.java
index c63b8f4..9a7c345 100644
--- a/services/core/java/com/android/server/BinderCallsStatsService.java
+++ b/services/core/java/com/android/server/BinderCallsStatsService.java
@@ -34,7 +34,6 @@
 import com.android.internal.os.BackgroundThread;
 import com.android.internal.os.BinderCallsStats;
 import com.android.internal.os.BinderInternal;
-import com.android.server.LocalServices;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -131,6 +130,7 @@
 
     public static class LifeCycle extends SystemService {
         private BinderCallsStatsService mService;
+        private BinderCallsStats mBinderCallsStats;
 
         public LifeCycle(Context context) {
             super(context);
@@ -138,9 +138,9 @@
 
         @Override
         public void onStart() {
-            BinderCallsStats binderCallsStats = new BinderCallsStats(new Random());
-            mService = new BinderCallsStatsService(binderCallsStats);
-            LocalServices.addService(Internal.class, new Internal(binderCallsStats));
+            mBinderCallsStats = new BinderCallsStats(new BinderCallsStats.Injector());
+            mService = new BinderCallsStatsService(mBinderCallsStats);
+            publishLocalService(Internal.class, new Internal(mBinderCallsStats));
             publishBinderService("binder_calls_stats", mService);
             boolean detailedTrackingEnabled = SystemProperties.getBoolean(
                     PERSIST_SYS_BINDER_CALLS_DETAILED_TRACKING, false);
@@ -149,7 +149,7 @@
                 Slog.i(TAG, "Enabled CPU usage tracking for binder calls. Controlled by "
                         + PERSIST_SYS_BINDER_CALLS_DETAILED_TRACKING
                         + " or via dumpsys binder_calls_stats --enable-detailed-tracking");
-                binderCallsStats.setDetailedTracking(true);
+                mBinderCallsStats.setDetailedTracking(true);
             }
         }
 
@@ -157,6 +157,7 @@
         public void onBootPhase(int phase) {
             if (SystemService.PHASE_SYSTEM_SERVICES_READY == phase) {
                 mService.systemReady(getContext());
+                mBinderCallsStats.systemReady(getContext());
             }
         }
     }
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index d1585cf..609ad75 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -3230,12 +3230,12 @@
     }
 
     @Override
-    public void onRecentTaskRemoved(TaskRecord task, boolean wasTrimmed) {
+    public void onRecentTaskRemoved(TaskRecord task, boolean wasTrimmed, boolean killProcess) {
         if (wasTrimmed) {
             // Task was trimmed from the recent tasks list -- remove the active task record as well
             // since the user won't really be able to go back to it
-            removeTaskByIdLocked(task.taskId, false /* killProcess */,
-                    false /* removeFromRecents */, !PAUSE_IMMEDIATELY, "recent-task-trimmed");
+            removeTaskByIdLocked(task.taskId, killProcess, false /* removeFromRecents */,
+                    !PAUSE_IMMEDIATELY, "recent-task-trimmed");
         }
         task.removedFromRecents();
     }
diff --git a/services/core/java/com/android/server/am/ActivityTaskManagerService.java b/services/core/java/com/android/server/am/ActivityTaskManagerService.java
index 748b2d2..54c2ee5 100644
--- a/services/core/java/com/android/server/am/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityTaskManagerService.java
@@ -27,6 +27,7 @@
 import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
 import static android.Manifest.permission.STOP_APP_SWITCHES;
 import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
+import static android.content.pm.PackageManager.FEATURE_PC;
 import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
 import static android.provider.Settings.Global.HIDE_ERROR_DIALOGS;
 import static android.provider.Settings.System.FONT_SCALE;
@@ -541,6 +542,7 @@
         final boolean forceRtl = Settings.Global.getInt(resolver, DEVELOPMENT_FORCE_RTL, 0) != 0;
         final boolean forceResizable = Settings.Global.getInt(
                 resolver, DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES, 0) != 0;
+        final boolean isPc = mContext.getPackageManager().hasSystemFeature(FEATURE_PC);
 
         // Transfer any global setting for forcing RTL layout, into a System Property
         SystemProperties.set(DEVELOPMENT_FORCE_RTL, forceRtl ? "1":"0");
@@ -573,6 +575,8 @@
             }
             mWindowManager.setForceResizableTasks(mForceResizableActivities);
             mWindowManager.setSupportsPictureInPicture(mSupportsPictureInPicture);
+            mWindowManager.setSupportsFreeformWindowManagement(mSupportsFreeformWindowManagement);
+            mWindowManager.setIsPc(isPc);
             // This happens before any activities are started, so we can change global configuration
             // in-place.
             updateConfigurationLocked(configuration, null, true);
@@ -1668,6 +1672,19 @@
     }
 
     @Override
+    public void removeAllVisibleRecentTasks() {
+        enforceCallerIsRecentsOrHasPermission(REMOVE_TASKS, "removeAllVisibleRecentTasks()");
+        synchronized (mGlobalLock) {
+            final long ident = Binder.clearCallingIdentity();
+            try {
+                getRecentTasks().removeAllVisibleTasks();
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
+    }
+
+    @Override
     public boolean shouldUpRecreateTask(IBinder token, String destAffinity) {
         synchronized (mGlobalLock) {
             final ActivityRecord srec = ActivityRecord.forTokenLocked(token);
diff --git a/services/core/java/com/android/server/am/RecentTasks.java b/services/core/java/com/android/server/am/RecentTasks.java
index fb6b5c1..e11e003 100644
--- a/services/core/java/com/android/server/am/RecentTasks.java
+++ b/services/core/java/com/android/server/am/RecentTasks.java
@@ -106,7 +106,6 @@
     private static final String TAG = TAG_WITH_CLASS_NAME ? "RecentTasks" : TAG_AM;
     private static final String TAG_RECENTS = TAG + POSTFIX_RECENTS;
     private static final String TAG_TASKS = TAG + POSTFIX_TASKS;
-    private static final boolean TRIMMED = true;
 
     private static final int DEFAULT_INITIAL_CAPACITY = 5;
 
@@ -134,7 +133,7 @@
         /**
          * Called when a task is removed from the recent tasks list.
          */
-        void onRecentTaskRemoved(TaskRecord task, boolean wasTrimmed);
+        void onRecentTaskRemoved(TaskRecord task, boolean wasTrimmed, boolean killProcess);
     }
 
     /**
@@ -322,9 +321,9 @@
         }
     }
 
-    private void notifyTaskRemoved(TaskRecord task, boolean wasTrimmed) {
+    private void notifyTaskRemoved(TaskRecord task, boolean wasTrimmed, boolean killProcess) {
         for (int i = 0; i < mCallbacks.size(); i++) {
-            mCallbacks.get(i).onRecentTaskRemoved(task, wasTrimmed);
+            mCallbacks.get(i).onRecentTaskRemoved(task, wasTrimmed, killProcess);
         }
     }
 
@@ -547,6 +546,16 @@
         }
     }
 
+    void removeAllVisibleTasks() {
+        for (int i = mTasks.size() - 1; i >= 0; --i) {
+            final TaskRecord tr = mTasks.get(i);
+            if (isVisibleRecentTask(tr)) {
+                mTasks.remove(i);
+                notifyTaskRemoved(tr, true /* wasTrimmed */, true /* killProcess */);
+            }
+        }
+    }
+
     void cleanupDisabledPackageTasksLocked(String packageName, Set<String> filterByClasses,
             int userId) {
         for (int i = mTasks.size() - 1; i >= 0; --i) {
@@ -1048,7 +1057,7 @@
      */
     void remove(TaskRecord task) {
         mTasks.remove(task);
-        notifyTaskRemoved(task, !TRIMMED);
+        notifyTaskRemoved(task, false /* wasTrimmed */, false /* killProcess */);
     }
 
     /**
@@ -1060,7 +1069,7 @@
         // Remove from the end of the list until we reach the max number of recents
         while (recentsCount > mGlobalMaxNumTasks) {
             final TaskRecord tr = mTasks.remove(recentsCount - 1);
-            notifyTaskRemoved(tr, TRIMMED);
+            notifyTaskRemoved(tr, true /* wasTrimmed */, false /* killProcess */);
             recentsCount--;
             if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, "Trimming over max-recents task=" + tr
                     + " max=" + mGlobalMaxNumTasks);
@@ -1114,7 +1123,7 @@
 
             // Task is no longer active, trim it from the list
             mTasks.remove(task);
-            notifyTaskRemoved(task, TRIMMED);
+            notifyTaskRemoved(task, true /* wasTrimmed */, false /* killProcess */);
             notifyTaskPersisterLocked(task, false /* flush */);
         }
     }
@@ -1268,7 +1277,7 @@
         // callbacks here.
         final TaskRecord removedTask = mTasks.remove(removeIndex);
         if (removedTask != task) {
-            notifyTaskRemoved(removedTask, !TRIMMED);
+            notifyTaskRemoved(removedTask, false /* wasTrimmed */, false /* killProcess */);
             if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, "Trimming task=" + removedTask
                     + " for addition of task=" + task);
         }
diff --git a/services/core/java/com/android/server/biometrics/common/AuthenticationClient.java b/services/core/java/com/android/server/biometrics/common/AuthenticationClient.java
index 02cc6d5..10a1b90 100644
--- a/services/core/java/com/android/server/biometrics/common/AuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/common/AuthenticationClient.java
@@ -114,7 +114,7 @@
         if (mBundle != null) {
             try {
                 if (acquiredInfo != BiometricConstants.BIOMETRIC_ACQUIRED_GOOD) {
-                    mStatusBarService.onFingerprintHelp(
+                    mStatusBarService.onBiometricHelp(
                             mFingerprintManager.getAcquiredString(acquiredInfo, vendorCode));
                 }
                 return false; // acquisition continues
@@ -143,7 +143,7 @@
         }
         if (mBundle != null) {
             try {
-                mStatusBarService.onFingerprintError(
+                mStatusBarService.onBiometricError(
                         mFingerprintManager.getErrorString(error, vendorCode));
             } catch (RemoteException e) {
                 Slog.e(getLogTag(), "Remote exception when sending error", e);
@@ -162,9 +162,9 @@
         if (mBundle != null) {
             try {
                 if (authenticated) {
-                    mStatusBarService.onFingerprintAuthenticated();
+                    mStatusBarService.onBiometricAuthenticated();
                 } else {
-                    mStatusBarService.onFingerprintHelp(getContext().getResources().getString(
+                    mStatusBarService.onBiometricHelp(getContext().getResources().getString(
                             com.android.internal.R.string.fingerprint_not_recognized));
                 }
             } catch (RemoteException e) {
@@ -224,7 +224,7 @@
 
                     // Send the lockout message to the system dialog
                     if (mBundle != null) {
-                        mStatusBarService.onFingerprintError(
+                        mStatusBarService.onBiometricError(
                                 mFingerprintManager.getErrorString(errorCode, 0 /* vendorCode */));
                     }
                 } catch (RemoteException e) {
@@ -263,7 +263,7 @@
             // If authenticating with system dialog, show the dialog
             if (mBundle != null) {
                 try {
-                    mStatusBarService.showFingerprintDialog(mBundle, mDialogReceiver);
+                    mStatusBarService.showBiometricDialog(mBundle, mDialogReceiver);
                 } catch (RemoteException e) {
                     Slog.e(getLogTag(), "Unable to show fingerprint dialog", e);
                 }
@@ -301,7 +301,7 @@
             // after BiometricPrompt.HIDE_DIALOG_DELAY
             if (mBundle != null && !mDialogDismissed && !mInLockout) {
                 try {
-                    mStatusBarService.hideFingerprintDialog();
+                    mStatusBarService.hideBiometricDialog();
                 } catch (RemoteException e) {
                     Slog.e(getLogTag(), "Unable to hide fingerprint dialog", e);
                 }
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 96f3a83..de53427 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -5273,6 +5273,7 @@
             ArrayList<Integer> userSentimentBefore = new ArrayList<>(N);
             ArrayList<Integer> suppressVisuallyBefore = new ArrayList<>(N);
             ArrayList<ArrayList<Notification.Action>> smartActionsBefore = new ArrayList<>(N);
+            ArrayList<ArrayList<CharSequence>> smartRepliesBefore = new ArrayList<>(N);
             for (int i = 0; i < N; i++) {
                 final NotificationRecord r = mNotificationList.get(i);
                 orderBefore.add(r.getKey());
@@ -5285,6 +5286,7 @@
                 userSentimentBefore.add(r.getUserSentiment());
                 suppressVisuallyBefore.add(r.getSuppressedVisualEffects());
                 smartActionsBefore.add(r.getSmartActions());
+                smartRepliesBefore.add(r.getSmartReplies());
                 mRankingHelper.extractSignals(r);
             }
             mRankingHelper.sort(mNotificationList);
@@ -5300,7 +5302,8 @@
                         || !Objects.equals(userSentimentBefore.get(i), r.getUserSentiment())
                         || !Objects.equals(suppressVisuallyBefore.get(i),
                         r.getSuppressedVisualEffects())
-                        || !Objects.equals(smartActionsBefore.get(i), r.getSmartActions())) {
+                        || !Objects.equals(smartActionsBefore.get(i), r.getSmartActions())
+                        || !Objects.equals(smartRepliesBefore.get(i), r.getSmartReplies())) {
                     mHandler.scheduleSendRankingUpdate();
                     return;
                 }
@@ -6297,6 +6300,7 @@
         Bundle userSentiment = new Bundle();
         Bundle hidden = new Bundle();
         Bundle smartActions = new Bundle();
+        Bundle smartReplies = new Bundle();
         for (int i = 0; i < N; i++) {
             NotificationRecord record = mNotificationList.get(i);
             if (!isVisibleToListener(record.sbn, info)) {
@@ -6325,6 +6329,7 @@
             userSentiment.putInt(key, record.getUserSentiment());
             hidden.putBoolean(key, record.isHidden());
             smartActions.putParcelableArrayList(key, record.getSmartActions());
+            smartReplies.putCharSequenceArrayList(key, record.getSmartReplies());
         }
         final int M = keys.size();
         String[] keysAr = keys.toArray(new String[M]);
@@ -6336,7 +6341,7 @@
         return new NotificationRankingUpdate(keysAr, interceptedKeysAr, visibilityOverrides,
                 suppressedVisualEffects, importanceAr, explanation, overrideGroupKeys,
                 channels, overridePeople, snoozeCriteria, showBadge, userSentiment, hidden,
-                smartActions);
+                smartActions, smartReplies);
     }
 
     boolean hasCompanionDevice(ManagedServiceInfo info) {
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index f32b338..469828b 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -162,6 +162,7 @@
     private String mGroupLogTag;
     private String mChannelIdLogTag;
     private ArrayList<Notification.Action> mSmartActions;
+    private ArrayList<CharSequence> mSmartReplies;
 
     private final List<Adjustment> mAdjustments;
     private final NotificationStats mStats;
@@ -637,6 +638,9 @@
                 if (signals.containsKey(Adjustment.KEY_SMART_ACTIONS)) {
                     setSmartActions(signals.getParcelableArrayList(Adjustment.KEY_SMART_ACTIONS));
                 }
+                if (signals.containsKey(Adjustment.KEY_SMART_REPLIES)) {
+                    setSmartReplies(signals.getCharSequenceArrayList(Adjustment.KEY_SMART_REPLIES));
+                }
             }
         }
     }
@@ -1064,6 +1068,14 @@
         return mSmartActions;
     }
 
+    public void setSmartReplies(ArrayList<CharSequence> smartReplies) {
+        mSmartReplies = smartReplies;
+    }
+
+    public ArrayList<CharSequence> getSmartReplies() {
+        return mSmartReplies;
+    }
+
     /**
      * @return all {@link Uri} that should have permission granted to whoever
      *         will be rendering it. This list has already been vetted to only
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 738b0ca..519881e 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -547,50 +547,50 @@
     }
 
     @Override
-    public void showFingerprintDialog(Bundle bundle, IBiometricPromptReceiver receiver) {
+    public void showBiometricDialog(Bundle bundle, IBiometricPromptReceiver receiver) {
         if (mBar != null) {
             try {
-                mBar.showFingerprintDialog(bundle, receiver);
+                mBar.showBiometricDialog(bundle, receiver);
             } catch (RemoteException ex) {
             }
         }
     }
 
     @Override
-    public void onFingerprintAuthenticated() {
+    public void onBiometricAuthenticated() {
         if (mBar != null) {
             try {
-                mBar.onFingerprintAuthenticated();
+                mBar.onBiometricAuthenticated();
             } catch (RemoteException ex) {
             }
         }
     }
 
     @Override
-    public void onFingerprintHelp(String message) {
+    public void onBiometricHelp(String message) {
         if (mBar != null) {
             try {
-                mBar.onFingerprintHelp(message);
+                mBar.onBiometricHelp(message);
             } catch (RemoteException ex) {
             }
         }
     }
 
     @Override
-    public void onFingerprintError(String error) {
+    public void onBiometricError(String error) {
         if (mBar != null) {
             try {
-                mBar.onFingerprintError(error);
+                mBar.onBiometricError(error);
             } catch (RemoteException ex) {
             }
         }
     }
 
     @Override
-    public void hideFingerprintDialog() {
+    public void hideBiometricDialog() {
         if (mBar != null) {
             try {
-                mBar.hideFingerprintDialog();
+                mBar.hideBiometricDialog();
             } catch (RemoteException ex) {
             }
         }
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index d7d5cf8..4ab06a2 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -1389,9 +1389,7 @@
         final int dw = displayInfo.logicalWidth;
         final int dh = displayInfo.logicalHeight;
         config.orientation = (dw <= dh) ? ORIENTATION_PORTRAIT : ORIENTATION_LANDSCAPE;
-        // TODO: Probably best to set this based on some setting in the display content object,
-        // so the display can be configured for things like fullscreen.
-        config.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+        config.windowConfiguration.setWindowingMode(getWindowingMode());
 
         final float density = mDisplayMetrics.density;
         config.screenWidthDp =
diff --git a/services/core/java/com/android/server/wm/DisplaySettings.java b/services/core/java/com/android/server/wm/DisplaySettings.java
index 97b64dc..bbb690f 100644
--- a/services/core/java/com/android/server/wm/DisplaySettings.java
+++ b/services/core/java/com/android/server/wm/DisplaySettings.java
@@ -19,12 +19,19 @@
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 
+import android.app.WindowConfiguration;
+import android.content.Context;
+import android.content.pm.PackageManager;
 import android.graphics.Rect;
 import android.os.Environment;
+import android.provider.Settings;
 import android.util.AtomicFile;
 import android.util.Slog;
 import android.util.Xml;
+import android.view.Display;
+import android.view.DisplayInfo;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.util.XmlUtils;
 
@@ -43,37 +50,48 @@
 /**
  * Current persistent settings about a display
  */
-public class DisplaySettings {
+class DisplaySettings {
     private static final String TAG = TAG_WITH_CLASS_NAME ? "DisplaySettings" : TAG_WM;
 
+    private final WindowManagerService mService;
     private final AtomicFile mFile;
     private final HashMap<String, Entry> mEntries = new HashMap<String, Entry>();
 
-    public static class Entry {
-        public final String name;
-        public int overscanLeft;
-        public int overscanTop;
-        public int overscanRight;
-        public int overscanBottom;
+    private static class Entry {
+        private final String name;
+        private int overscanLeft;
+        private int overscanTop;
+        private int overscanRight;
+        private int overscanBottom;
+        private int windowingMode = WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 
-        public Entry(String _name) {
+        private Entry(String _name) {
             name = _name;
         }
     }
 
-    public DisplaySettings() {
-        File dataDir = Environment.getDataDirectory();
-        File systemDir = new File(dataDir, "system");
-        mFile = new AtomicFile(new File(systemDir, "display_settings.xml"), "wm-displays");
+    DisplaySettings(WindowManagerService service) {
+        this(service, new File(Environment.getDataDirectory(), "system"));
     }
 
-    public void getOverscanLocked(String name, String uniqueId, Rect outRect) {
+    @VisibleForTesting
+    DisplaySettings(WindowManagerService service, File folder) {
+        mService = service;
+        mFile = new AtomicFile(new File(folder, "display_settings.xml"), "wm-displays");
+    }
+
+    private Entry getEntry(String name, String uniqueId) {
         // Try to get the entry with the unique if possible.
         // Else, fall back on the display name.
         Entry entry;
         if (uniqueId == null || (entry = mEntries.get(uniqueId)) == null) {
             entry = mEntries.get(name);
         }
+        return entry;
+    }
+
+    private void getOverscanLocked(String name, String uniqueId, Rect outRect) {
+        final Entry entry = getEntry(name, uniqueId);
         if (entry != null) {
             outRect.left = entry.overscanLeft;
             outRect.top = entry.overscanTop;
@@ -84,7 +102,7 @@
         }
     }
 
-    public void setOverscanLocked(String uniqueId, String name, int left, int top, int right,
+    void setOverscanLocked(String uniqueId, String name, int left, int top, int right,
             int bottom) {
         if (left == 0 && top == 0 && right == 0 && bottom == 0) {
             // Right now all we are storing is overscan; if there is no overscan,
@@ -105,7 +123,47 @@
         entry.overscanBottom = bottom;
     }
 
-    public void readSettingsLocked() {
+    private int getWindowingModeLocked(String name, String uniqueId, int displayId) {
+        final Entry entry = getEntry(name, uniqueId);
+        int windowingMode = entry != null ? entry.windowingMode
+                : WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+        // This display used to be in freeform, but we don't support freeform anymore, so fall
+        // back to fullscreen.
+        if (windowingMode == WindowConfiguration.WINDOWING_MODE_FREEFORM
+                && !mService.mSupportsFreeformWindowManagement) {
+            return WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+        }
+        // No record is present so use default windowing mode policy.
+        if (windowingMode == WindowConfiguration.WINDOWING_MODE_UNDEFINED) {
+            if (displayId == Display.DEFAULT_DISPLAY) {
+                windowingMode = (mService.mIsPc && mService.mSupportsFreeformWindowManagement)
+                        ? WindowConfiguration.WINDOWING_MODE_FREEFORM
+                        : WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+            } else {
+                windowingMode = mService.mSupportsFreeformWindowManagement
+                        ? WindowConfiguration.WINDOWING_MODE_FREEFORM
+                        : WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+            }
+        }
+        return windowingMode;
+    }
+
+    void applySettingsToDisplayLocked(DisplayContent dc) {
+        final DisplayInfo displayInfo = dc.getDisplayInfo();
+
+        // Setting windowing mode first, because it may override overscan values later.
+        dc.setWindowingMode(getWindowingModeLocked(displayInfo.name, displayInfo.uniqueId,
+                dc.getDisplayId()));
+
+        final Rect rect = new Rect();
+        getOverscanLocked(displayInfo.name, displayInfo.uniqueId, rect);
+        displayInfo.overscanLeft = rect.left;
+        displayInfo.overscanTop = rect.top;
+        displayInfo.overscanRight = rect.right;
+        displayInfo.overscanBottom = rect.bottom;
+    }
+
+    void readSettingsLocked() {
         FileInputStream stream;
         try {
             stream = mFile.openRead();
@@ -169,11 +227,15 @@
     }
 
     private int getIntAttribute(XmlPullParser parser, String name) {
+        return getIntAttribute(parser, name, 0 /* defaultValue */);
+    }
+
+    private int getIntAttribute(XmlPullParser parser, String name, int defaultValue) {
         try {
             String str = parser.getAttributeValue(null, name);
-            return str != null ? Integer.parseInt(str) : 0;
+            return str != null ? Integer.parseInt(str) : defaultValue;
         } catch (NumberFormatException e) {
-            return 0;
+            return defaultValue;
         }
     }
 
@@ -186,12 +248,14 @@
             entry.overscanTop = getIntAttribute(parser, "overscanTop");
             entry.overscanRight = getIntAttribute(parser, "overscanRight");
             entry.overscanBottom = getIntAttribute(parser, "overscanBottom");
+            entry.windowingMode = getIntAttribute(parser, "windowingMode",
+                    WindowConfiguration.WINDOWING_MODE_UNDEFINED);
             mEntries.put(name, entry);
         }
         XmlUtils.skipCurrentTag(parser);
     }
 
-    public void writeSettingsLocked() {
+    void writeSettingsLocked() {
         FileOutputStream stream;
         try {
             stream = mFile.startWrite();
@@ -221,6 +285,9 @@
                 if (entry.overscanBottom != 0) {
                     out.attribute(null, "overscanBottom", Integer.toString(entry.overscanBottom));
                 }
+                if (entry.windowingMode != WindowConfiguration.WINDOWING_MODE_UNDEFINED) {
+                    out.attribute(null, "windowingMode", Integer.toString(entry.windowingMode));
+                }
                 out.endTag(null, "display");
             }
 
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index ca4fa64..a6bda37 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -224,16 +224,11 @@
 
         if (DEBUG_DISPLAY) Slog.v(TAG_WM, "Adding display=" + display);
 
-        final DisplayInfo displayInfo = dc.getDisplayInfo();
-        final Rect rect = new Rect();
-        mService.mDisplaySettings.getOverscanLocked(displayInfo.name, displayInfo.uniqueId, rect);
-        displayInfo.overscanLeft = rect.left;
-        displayInfo.overscanTop = rect.top;
-        displayInfo.overscanRight = rect.right;
-        displayInfo.overscanBottom = rect.bottom;
+        mService.mDisplaySettings.applySettingsToDisplayLocked(dc);
+
         if (mService.mDisplayManagerInternal != null) {
             mService.mDisplayManagerInternal.setDisplayInfoOverrideFromWindowManager(
-                    displayId, displayInfo);
+                    displayId, dc.getDisplayInfo());
             dc.configureDisplayPolicy();
 
             // Tap Listeners are supported for:
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index ea68e17..ab735af 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -565,6 +565,8 @@
 
     boolean mForceResizableTasks = false;
     boolean mSupportsPictureInPicture = false;
+    boolean mSupportsFreeformWindowManagement = false;
+    boolean mIsPc = false;
 
     boolean mDisableTransitionAnimation = false;
 
@@ -953,7 +955,7 @@
                 com.android.internal.R.bool.config_disableTransitionAnimation);
         mInputManager = inputManager; // Must be before createDisplayContentLocked.
         mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
-        mDisplaySettings = new DisplaySettings();
+        mDisplaySettings = new DisplaySettings(this);
         mDisplaySettings.readSettingsLocked();
 
         mPolicy = policy;
@@ -6910,6 +6912,18 @@
         }
     }
 
+    public void setSupportsFreeformWindowManagement(boolean supportsFreeformWindowManagement) {
+        synchronized (mWindowMap) {
+            mSupportsFreeformWindowManagement = supportsFreeformWindowManagement;
+        }
+    }
+
+    public void setIsPc(boolean isPc) {
+        synchronized (mWindowMap) {
+            mIsPc = isPc;
+        }
+    }
+
     static int dipToPixel(int dip, DisplayMetrics displayMetrics) {
         return (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dip, displayMetrics);
     }
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 6385229..9f394ac 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -3870,7 +3870,8 @@
         final boolean isAccessibilityOverlay =
                 windowInfo.type == WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
         if (TextUtils.isEmpty(windowInfo.title) && (isPanelWindow || isAccessibilityOverlay)) {
-            windowInfo.title = mAttrs.getTitle();
+            final CharSequence title = mAttrs.getTitle();
+            windowInfo.title = TextUtils.isEmpty(title) ? null : title;
         }
         windowInfo.accessibilityIdOfAnchor = mAttrs.accessibilityIdOfAnchor;
         windowInfo.focused = isFocused();
diff --git a/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java b/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java
index 227a70f..ee484d6 100644
--- a/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java
@@ -17,6 +17,7 @@
 package com.android.server.am;
 
 import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
@@ -43,6 +44,7 @@
 import android.app.ActivityManager.RecentTaskInfo;
 import android.app.ActivityManager.RunningTaskInfo;
 import android.app.ActivityTaskManager;
+import android.app.WindowConfiguration;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.pm.PackageManager;
@@ -51,7 +53,6 @@
 import android.content.res.Configuration;
 import android.graphics.Rect;
 import android.os.Bundle;
-import android.os.Debug;
 import android.os.Looper;
 import android.os.RemoteException;
 import android.os.SystemClock;
@@ -276,13 +277,11 @@
     public void testAddTaskCompatibleActivityType_expectRemove() throws Exception {
         // Test with undefined activity type since the type is not persisted by the task persister
         // and we want to ensure that a new task will match a restored task
-        Configuration config1 = new Configuration();
-        config1.windowConfiguration.setActivityType(ACTIVITY_TYPE_UNDEFINED);
         TaskRecord task1 = createTaskBuilder(".Task1")
                 .setFlags(FLAG_ACTIVITY_NEW_TASK)
                 .setStack(mStack)
                 .build();
-        task1.onConfigurationChanged(config1);
+        setTaskActivityType(task1, ACTIVITY_TYPE_UNDEFINED);
         assertTrue(task1.getActivityType() == ACTIVITY_TYPE_UNDEFINED);
         mRecentTasks.add(task1);
         mCallbacksRecorder.clear();
@@ -302,14 +301,12 @@
 
     @Test
     public void testAddTaskCompatibleActivityTypeDifferentUser_expectNoRemove() throws Exception {
-        Configuration config1 = new Configuration();
-        config1.windowConfiguration.setActivityType(ACTIVITY_TYPE_UNDEFINED);
         TaskRecord task1 = createTaskBuilder(".Task1")
                 .setFlags(FLAG_ACTIVITY_NEW_TASK)
                 .setStack(mStack)
                 .setUserId(TEST_USER_0_ID)
                 .build();
-        task1.onConfigurationChanged(config1);
+        setTaskActivityType(task1, ACTIVITY_TYPE_UNDEFINED);
         assertTrue(task1.getActivityType() == ACTIVITY_TYPE_UNDEFINED);
         mRecentTasks.add(task1);
         mCallbacksRecorder.clear();
@@ -329,24 +326,20 @@
 
     @Test
     public void testAddTaskCompatibleWindowingMode_expectRemove() throws Exception {
-        Configuration config1 = new Configuration();
-        config1.windowConfiguration.setWindowingMode(WINDOWING_MODE_UNDEFINED);
         TaskRecord task1 = createTaskBuilder(".Task1")
                 .setFlags(FLAG_ACTIVITY_NEW_TASK)
                 .setStack(mStack)
                 .build();
-        task1.onConfigurationChanged(config1);
+        setTaskWindowingMode(task1, WINDOWING_MODE_UNDEFINED);
         assertTrue(task1.getWindowingMode() == WINDOWING_MODE_UNDEFINED);
         mRecentTasks.add(task1);
         mCallbacksRecorder.clear();
 
-        Configuration config2 = new Configuration();
-        config2.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
         TaskRecord task2 = createTaskBuilder(".Task1")
                 .setFlags(FLAG_ACTIVITY_NEW_TASK)
                 .setStack(mStack)
                 .build();
-        task2.onConfigurationChanged(config2);
+        setTaskWindowingMode(task2, WINDOWING_MODE_FULLSCREEN);
         assertTrue(task2.getWindowingMode() == WINDOWING_MODE_FULLSCREEN);
         mRecentTasks.add(task2);
 
@@ -359,23 +352,19 @@
 
     @Test
     public void testAddTaskIncompatibleWindowingMode_expectNoRemove() throws Exception {
-        Configuration config1 = new Configuration();
-        config1.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
         TaskRecord task1 = createTaskBuilder(".Task1")
                 .setFlags(FLAG_ACTIVITY_NEW_TASK)
                 .setStack(mStack)
                 .build();
-        task1.onConfigurationChanged(config1);
+        setTaskWindowingMode(task1, WINDOWING_MODE_FULLSCREEN);
         assertTrue(task1.getWindowingMode() == WINDOWING_MODE_FULLSCREEN);
         mRecentTasks.add(task1);
 
-        Configuration config2 = new Configuration();
-        config2.windowConfiguration.setWindowingMode(WINDOWING_MODE_PINNED);
         TaskRecord task2 = createTaskBuilder(".Task1")
                 .setFlags(FLAG_ACTIVITY_NEW_TASK)
                 .setStack(mStack)
                 .build();
-        task2.onConfigurationChanged(config2);
+        setTaskWindowingMode(task2, WINDOWING_MODE_PINNED);
         assertTrue(task2.getWindowingMode() == WINDOWING_MODE_PINNED);
         mRecentTasks.add(task2);
 
@@ -644,6 +633,43 @@
     }
 
     @Test
+    public void testRemoveAllVisibleTasks() throws Exception {
+        mRecentTasks.setParameters(-1 /* min */, 3 /* max */, 100 /* ms */);
+
+        // Create some set of tasks, some of which are visible and some are not
+        TaskRecord t1 = createTaskBuilder("com.android.pkg1", ".Task1").build();
+        mRecentTasks.add(t1);
+        mRecentTasks.add(setTaskActivityType(
+                createTaskBuilder("com.android.pkg1", ".HomeTask").build(),
+                ACTIVITY_TYPE_HOME));
+        TaskRecord t2 = createTaskBuilder("com.android.pkg2", ".Task2").build();
+        mRecentTasks.add(t2);
+        mRecentTasks.add(setTaskWindowingMode(
+                createTaskBuilder("com.android.pkg1", ".PipTask").build(),
+                WINDOWING_MODE_PINNED));
+        TaskRecord t3 = createTaskBuilder("com.android.pkg3", ".Task3").build();
+        mRecentTasks.add(t3);
+
+        // Create some more tasks that are out of visible range, but are still visible
+        TaskRecord t4 = createTaskBuilder("com.android.pkg3", ".Task4").build();
+        mRecentTasks.add(t4);
+        TaskRecord t5 = createTaskBuilder("com.android.pkg3", ".Task5").build();
+        mRecentTasks.add(t5);
+
+        // Create some more tasks that are out of the active session range, but are still visible
+        TaskRecord t6 = createTaskBuilder("com.android.pkg3", ".Task6").build();
+        t6.lastActiveTime = SystemClock.elapsedRealtime() - 200;
+        mRecentTasks.add(t6);
+        TaskRecord t7 = createTaskBuilder("com.android.pkg3", ".Task7").build();
+        t7.lastActiveTime = SystemClock.elapsedRealtime() - 200;
+        mRecentTasks.add(t7);
+
+        // Remove all the visible tasks and ensure that they are removed
+        mRecentTasks.removeAllVisibleTasks();
+        assertTrimmed(t1, t2, t3, t4, t5, t6, t7);
+    }
+
+    @Test
     public void testNotRecentsComponent_denyApiAccess() throws Exception {
         doReturn(PackageManager.PERMISSION_DENIED).when(mService)
                 .checkGetTasksPermission(anyString(), anyInt(), anyInt());
@@ -754,6 +780,22 @@
         return task;
     }
 
+    private TaskRecord setTaskActivityType(TaskRecord task,
+            @WindowConfiguration.ActivityType int activityType) {
+        Configuration config1 = new Configuration();
+        config1.windowConfiguration.setActivityType(activityType);
+        task.onConfigurationChanged(config1);
+        return task;
+    }
+
+    private TaskRecord setTaskWindowingMode(TaskRecord task,
+            @WindowConfiguration.WindowingMode int windowingMode) {
+        Configuration config1 = new Configuration();
+        config1.windowConfiguration.setWindowingMode(windowingMode);
+        task.onConfigurationChanged(config1);
+        return task;
+    }
+
     private boolean arrayContainsUser(int[] userIds, int targetUserId) {
         Arrays.sort(userIds);
         return Arrays.binarySearch(userIds, targetUserId) >= 0;
@@ -880,7 +922,7 @@
         }
 
         @Override
-        public void onRecentTaskRemoved(TaskRecord task, boolean wasTrimmed) {
+        public void onRecentTaskRemoved(TaskRecord task, boolean wasTrimmed, boolean killProcess) {
             if (wasTrimmed) {
                 trimmed.add(task);
             }
diff --git a/services/tests/servicestests/src/com/android/server/wm/DisplaySettingsTests.java b/services/tests/servicestests/src/com/android/server/wm/DisplaySettingsTests.java
new file mode 100644
index 0000000..0ea56ed
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/wm/DisplaySettingsTests.java
@@ -0,0 +1,180 @@
+/*
+ * Copyright (C) 2018 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.server.wm;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+
+import android.app.WindowConfiguration;
+import android.platform.test.annotations.Presubmit;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.Display;
+import android.view.DisplayInfo;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+
+@SmallTest
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class DisplaySettingsTests extends WindowTestsBase {
+
+    private File mTestFolder;
+    private DisplaySettings mTarget;
+
+    private DisplayContent mPrimaryDisplay;
+    private DisplayContent mSecondaryDisplay;
+
+    @Before
+    public void setUp() throws Exception {
+        super.setUp();
+
+        mTestFolder = InstrumentationRegistry.getContext().getCacheDir();
+        deleteRecursively(mTestFolder);
+
+        sWm.setSupportsFreeformWindowManagement(false);
+        sWm.setIsPc(false);
+
+        mTarget = new DisplaySettings(sWm, mTestFolder);
+        mTarget.readSettingsLocked();
+
+        mPrimaryDisplay = sWm.getDefaultDisplayContentLocked();
+        mSecondaryDisplay = createNewDisplay();
+        assertNotEquals(Display.DEFAULT_DISPLAY, mSecondaryDisplay.getDisplayId());
+    }
+
+    @Test
+    public void testPrimaryDisplayDefaultToFullscreenWithoutFreeformSupport() {
+        mTarget.applySettingsToDisplayLocked(mPrimaryDisplay);
+
+        assertEquals(WindowConfiguration.WINDOWING_MODE_FULLSCREEN,
+                mPrimaryDisplay.getWindowingMode());
+    }
+
+    @Test
+    public void testPrimaryDisplayDefaultToFullscreenWithFreeformSupportNonPc() {
+        sWm.setSupportsFreeformWindowManagement(true);
+
+        mTarget.applySettingsToDisplayLocked(mPrimaryDisplay);
+
+        assertEquals(WindowConfiguration.WINDOWING_MODE_FULLSCREEN,
+                mPrimaryDisplay.getWindowingMode());
+    }
+
+    @Test
+    public void testPrimaryDisplayDefaultToFreeformWithFreeformIsPc() {
+        sWm.setSupportsFreeformWindowManagement(true);
+        sWm.setIsPc(true);
+
+        mTarget.applySettingsToDisplayLocked(mPrimaryDisplay);
+
+        assertEquals(WindowConfiguration.WINDOWING_MODE_FREEFORM,
+                mPrimaryDisplay.getWindowingMode());
+    }
+
+    @Test
+    public void testSecondaryDisplayDefaultToFullscreenWithoutFreeformSupport() {
+        mTarget.applySettingsToDisplayLocked(mSecondaryDisplay);
+
+        assertEquals(WindowConfiguration.WINDOWING_MODE_FULLSCREEN,
+                mSecondaryDisplay.getWindowingMode());
+    }
+
+    @Test
+    public void testSecondaryDisplayDefaultToFreeformWithFreeformSupportNonPc() {
+        sWm.setSupportsFreeformWindowManagement(true);
+
+        mTarget.applySettingsToDisplayLocked(mSecondaryDisplay);
+
+        assertEquals(WindowConfiguration.WINDOWING_MODE_FREEFORM,
+                mSecondaryDisplay.getWindowingMode());
+    }
+
+    @Test
+    public void testSecondaryDisplayDefaultToFreeformWithFreeformSupportIsPc() {
+        sWm.setSupportsFreeformWindowManagement(true);
+        sWm.setIsPc(true);
+
+        mTarget.applySettingsToDisplayLocked(mSecondaryDisplay);
+
+        assertEquals(WindowConfiguration.WINDOWING_MODE_FREEFORM,
+                mSecondaryDisplay.getWindowingMode());
+    }
+
+    @Test
+    public void testDefaultToZeroOverscan() {
+        mTarget.applySettingsToDisplayLocked(mPrimaryDisplay);
+
+        assertOverscan(mPrimaryDisplay, 0 /* left */, 0 /* top */, 0 /* right */, 0 /* bottom */);
+    }
+
+    @Test
+    public void testPersistOverscanInSameInstance() {
+        final DisplayInfo info = mPrimaryDisplay.getDisplayInfo();
+        mTarget.setOverscanLocked(info.uniqueId, info.name, 1 /* left */, 2 /* top */,
+                3 /* right */, 4 /* bottom */);
+
+        mTarget.applySettingsToDisplayLocked(mPrimaryDisplay);
+
+        assertOverscan(mPrimaryDisplay, 1 /* left */, 2 /* top */, 3 /* right */, 4 /* bottom */);
+    }
+
+    @Test
+    public void testPersistOverscanAcrossInstances() {
+        final DisplayInfo info = mPrimaryDisplay.getDisplayInfo();
+        mTarget.setOverscanLocked(info.uniqueId, info.name, 1 /* left */, 2 /* top */,
+                3 /* right */, 4 /* bottom */);
+        mTarget.writeSettingsLocked();
+
+        DisplaySettings target = new DisplaySettings(sWm, mTestFolder);
+        target.readSettingsLocked();
+
+        target.applySettingsToDisplayLocked(mPrimaryDisplay);
+
+        assertOverscan(mPrimaryDisplay, 1 /* left */, 2 /* top */, 3 /* right */, 4 /* bottom */);
+    }
+
+    private static void assertOverscan(DisplayContent display, int left, int top, int right,
+            int bottom) {
+        final DisplayInfo info = display.getDisplayInfo();
+
+        assertEquals(left, info.overscanLeft);
+        assertEquals(top, info.overscanTop);
+        assertEquals(right, info.overscanRight);
+        assertEquals(bottom, info.overscanBottom);
+    }
+
+    private static boolean deleteRecursively(File file) {
+        if (file.isFile()) {
+            return file.delete();
+        }
+
+        boolean fullyDeleted = true;
+        final File[] files = file.listFiles();
+        for (File child : files) {
+            fullyDeleted &= deleteRecursively(child);
+        }
+        fullyDeleted &= file.delete();
+        return fullyDeleted;
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/wm/ScreenDecorWindowTests.java b/services/tests/servicestests/src/com/android/server/wm/ScreenDecorWindowTests.java
index a2ccee4..bfc99c8 100644
--- a/services/tests/servicestests/src/com/android/server/wm/ScreenDecorWindowTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/ScreenDecorWindowTests.java
@@ -255,16 +255,32 @@
      * Asserts the top inset of {@param activity} is equal to {@param expected} waiting as needed.
      */
     private void assertTopInsetEquals(Activity activity, int expected) throws Exception {
-        waitFor(() -> getInsets(activity).getSystemWindowInsetTop() == expected);
+        waitForTopInsetEqual(activity, expected);
         assertEquals(expected, getInsets(activity).getSystemWindowInsetTop());
     }
 
+    private void waitForTopInsetEqual(Activity activity, int expected) {
+        waitFor(() -> getInsets(activity).getSystemWindowInsetTop() == expected);
+    }
+
     /**
      * Asserts the inset at {@param side} of {@param activity} is equal to {@param expected}
      * waiting as needed.
      */
     private void assertInsetGreaterOrEqual(Activity activity, int side, int expected)
             throws Exception {
+        waitForInsetGreaterOrEqual(activity, side, expected);
+
+        final WindowInsets insets = getInsets(activity);
+        switch (side) {
+            case TOP: assertGreaterOrEqual(insets.getSystemWindowInsetTop(), expected); break;
+            case BOTTOM: assertGreaterOrEqual(insets.getSystemWindowInsetBottom(), expected); break;
+            case LEFT: assertGreaterOrEqual(insets.getSystemWindowInsetLeft(), expected); break;
+            case RIGHT: assertGreaterOrEqual(insets.getSystemWindowInsetRight(), expected); break;
+        }
+    }
+
+    private void waitForInsetGreaterOrEqual(Activity activity, int side, int expected) {
         waitFor(() -> {
             final WindowInsets insets = getInsets(activity);
             switch (side) {
@@ -275,14 +291,6 @@
                 default: return true;
             }
         });
-
-        final WindowInsets insets = getInsets(activity);
-        switch (side) {
-            case TOP: assertGreaterOrEqual(insets.getSystemWindowInsetTop(), expected); break;
-            case BOTTOM: assertGreaterOrEqual(insets.getSystemWindowInsetBottom(), expected); break;
-            case LEFT: assertGreaterOrEqual(insets.getSystemWindowInsetLeft(), expected); break;
-            case RIGHT: assertGreaterOrEqual(insets.getSystemWindowInsetRight(), expected); break;
-        }
     }
 
     /** Asserts that the first entry is greater than or equal to the second entry. */
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java
index 4864332..431d1a7 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java
@@ -121,13 +121,52 @@
         mStubStack = new TaskStack(sWm, 0, null);
     }
 
-    public void assertRect(Rect rect, int left, int top, int right, int bottom) {
+    // Do not use this function directly in the tests below. Instead, use more explicit function
+    // such as assertFlame().
+    private void assertRect(Rect rect, int left, int top, int right, int bottom) {
         assertEquals(left, rect.left);
         assertEquals(top, rect.top);
         assertEquals(right, rect.right);
         assertEquals(bottom, rect.bottom);
     }
 
+    private void assertContentInset(WindowState w, int left, int top, int right, int bottom) {
+        assertRect(w.mContentInsets, left, top, right, bottom);
+    }
+
+    private void assertVisibleInset(WindowState w, int left, int top, int right, int bottom) {
+        assertRect(w.mVisibleInsets, left, top, right, bottom);
+    }
+
+    private void assertStableInset(WindowState w, int left, int top, int right, int bottom) {
+        assertRect(w.mStableInsets, left, top, right, bottom);
+    }
+
+    private void assertFrame(WindowState w, int left, int top, int right, int bottom) {
+        assertRect(w.getFrameLw(), left, top, right, bottom);
+    }
+
+    private void assertContentFrame(WindowState w, Rect expectedRect) {
+        assertRect(w.getContentFrameLw(), expectedRect.left, expectedRect.top, expectedRect.right,
+                expectedRect.bottom);
+    }
+
+    private void assertVisibleFrame(WindowState w, Rect expectedRect) {
+        assertRect(w.getVisibleFrameLw(), expectedRect.left, expectedRect.top, expectedRect.right,
+                expectedRect.bottom);
+    }
+
+    private void assertStableFrame(WindowState w, Rect expectedRect) {
+        assertRect(w.getStableFrameLw(), expectedRect.left, expectedRect.top, expectedRect.right,
+                expectedRect.bottom);
+    }
+
+    private void assertPolicyCrop(WindowStateWithTask w, int left, int top, int right, int bottom) {
+        Rect policyCrop = new Rect();
+        w.calculatePolicyCrop(policyCrop);
+        assertRect(policyCrop, left, top, right, bottom);
+    }
+
     @Test
     public void testLayoutInFullscreenTaskInsets() throws Exception {
         Task task = new TaskWithBounds(null); // fullscreen task doesn't use bounds for computeFrame
@@ -163,14 +202,13 @@
         // and stable frames work the same way.
         final WindowFrames windowFrames = new WindowFrames(pf, df, of, cf, vf, dcf, sf, mEmptyRect);
         w.computeFrameLw(windowFrames);
-        assertRect(w.getFrameLw(),0, 0, 1000, 1000);
-        assertRect(w.mContentInsets, 0, topContentInset, 0, bottomContentInset);
-        assertRect(w.mVisibleInsets, 0, topVisibleInset, 0, bottomVisibleInset);
-        assertRect(w.mStableInsets, leftStableInset, 0, rightStableInset, 0);
-        // The frames remain as passed in shrunk to the window frame
-        assertTrue(cf.equals(w.getContentFrameLw()));
-        assertTrue(vf.equals(w.getVisibleFrameLw()));
-        assertTrue(sf.equals(w.getStableFrameLw()));
+        assertFrame(w, 0, 0, 1000, 1000);
+        assertContentInset(w, 0, topContentInset, 0, bottomContentInset);
+        assertVisibleInset(w, 0, topVisibleInset, 0, bottomVisibleInset);
+        assertStableInset(w, leftStableInset, 0, rightStableInset, 0);
+        assertContentFrame(w, cf);
+        assertVisibleFrame(w, vf);
+        assertStableFrame(w, sf);
         // On the other hand getFrame() doesn't extend past cf we won't get any insets
         w.mAttrs.x = 100;
         w.mAttrs.y = 100;
@@ -178,12 +216,12 @@
         w.mRequestedWidth = 100;
         w.mRequestedHeight = 100;
         w.computeFrameLw(windowFrames);
-        assertRect(w.getFrameLw(), 100, 100, 200, 200);
-        assertRect(w.mContentInsets, 0, 0, 0, 0);
+        assertFrame(w, 100, 100, 200, 200);
+        assertContentInset(w, 0, 0, 0, 0);
         // In this case the frames are shrunk to the window frame.
-        assertTrue(w.getFrameLw().equals(w.getContentFrameLw()));
-        assertTrue(w.getFrameLw().equals(w.getVisibleFrameLw()));
-        assertTrue(w.getFrameLw().equals(w.getStableFrameLw()));
+        assertContentFrame(w, w.getFrameLw());
+        assertVisibleFrame(w, w.getFrameLw());
+        assertStableFrame(w, w.getFrameLw());
     }
 
     @Test
@@ -200,7 +238,7 @@
         // so we expect it to fill the entire available frame.
         final WindowFrames windowFrames = new WindowFrames(pf, pf, pf, pf, pf, pf, pf, pf);
         w.computeFrameLw(windowFrames);
-        assertRect(w.getFrameLw(), 0, 0, 1000, 1000);
+        assertFrame(w, 0, 0, 1000, 1000);
 
         // It can select various widths and heights within the bounds.
         // Strangely the window attribute width is ignored for normal windows
@@ -210,14 +248,14 @@
         w.computeFrameLw(windowFrames);
         // Explicit width and height without requested width/height
         // gets us nothing.
-        assertRect(w.getFrameLw(), 0, 0, 0, 0);
+        assertFrame(w, 0, 0, 0, 0);
 
         w.mRequestedWidth = 300;
         w.mRequestedHeight = 300;
         w.computeFrameLw(windowFrames);
         // With requestedWidth/Height we can freely choose our size within the
         // parent bounds.
-        assertRect(w.getFrameLw(), 0, 0, 300, 300);
+        assertFrame(w, 0, 0, 300, 300);
 
         // With FLAG_SCALED though, requestedWidth/height is used to control
         // the unscaled surface size, and mAttrs.width/height becomes the
@@ -228,14 +266,14 @@
         w.mAttrs.width = 100;
         w.mAttrs.height = 100;
         w.computeFrameLw(windowFrames);
-        assertRect(w.getFrameLw(), 0, 0, 100, 100);
+        assertFrame(w, 0, 0, 100, 100);
         w.mAttrs.flags = 0;
 
         // But sizes too large will be clipped to the containing frame
         w.mRequestedWidth = 1200;
         w.mRequestedHeight = 1200;
         w.computeFrameLw(windowFrames);
-        assertRect(w.getFrameLw(), 0, 0, 1000, 1000);
+        assertFrame(w, 0, 0, 1000, 1000);
 
         // Before they are clipped though windows will be shifted
         w.mAttrs.x = 300;
@@ -243,7 +281,7 @@
         w.mRequestedWidth = 1000;
         w.mRequestedHeight = 1000;
         w.computeFrameLw(windowFrames);
-        assertRect(w.getFrameLw(), 0, 0, 1000, 1000);
+        assertFrame(w, 0, 0, 1000, 1000);
 
         // If there is room to move around in the parent frame the window will be shifted according
         // to gravity.
@@ -253,16 +291,16 @@
         w.mRequestedHeight = 300;
         w.mAttrs.gravity = Gravity.RIGHT | Gravity.TOP;
         w.computeFrameLw(windowFrames);
-        assertRect(w.getFrameLw(), 700, 0, 1000, 300);
+         assertFrame(w, 700, 0, 1000, 300);
         w.mAttrs.gravity = Gravity.RIGHT | Gravity.BOTTOM;
         w.computeFrameLw(windowFrames);
-        assertRect(w.getFrameLw(), 700, 700, 1000, 1000);
+        assertFrame(w, 700, 700, 1000, 1000);
         // Window specified  x and y are interpreted as offsets in the opposite
         // direction of gravity
         w.mAttrs.x = 100;
         w.mAttrs.y = 100;
         w.computeFrameLw(windowFrames);
-        assertRect(w.getFrameLw(), 600, 600, 900, 900);
+        assertFrame(w, 600, 600, 900, 900);
     }
 
     @Test
@@ -286,24 +324,23 @@
         w.computeFrameLw(windowFrames);
         // For non fullscreen tasks the containing frame is based off the
         // task bounds not the parent frame.
-        assertRect(w.getFrameLw(), taskLeft, taskTop, taskRight, taskBottom);
-        assertRect(w.getContentFrameLw(), taskLeft, taskTop, taskRight, taskBottom);
-        assertRect(w.mContentInsets, 0, 0, 0, 0);
+        assertFrame(w, taskLeft, taskTop, taskRight, taskBottom);
+        assertContentFrame(w, taskBounds);
+        assertContentInset(w, 0, 0, 0, 0);
 
         pf.set(0, 0, logicalWidth, logicalHeight);
         // We still produce insets against the containing frame the same way.
         final int cfRight = logicalWidth / 2;
         final int cfBottom = logicalHeight / 2;
         final Rect cf = new Rect(0, 0, cfRight, cfBottom);
-
         windowFrames.setFrames(pf, pf, pf, cf, cf, pf, cf, mEmptyRect);
         w.computeFrameLw(windowFrames);
-        assertRect(w.getFrameLw(), taskLeft, taskTop, taskRight, taskBottom);
+        assertFrame(w, taskLeft, taskTop, taskRight, taskBottom);
         int contentInsetRight = taskRight - cfRight;
         int contentInsetBottom = taskBottom - cfBottom;
-        assertRect(w.mContentInsets, 0, 0, contentInsetRight, contentInsetBottom);
-        assertRect(w.getContentFrameLw(), taskLeft, taskTop, taskRight - contentInsetRight,
-                taskBottom - contentInsetBottom);
+        assertContentInset(w, 0, 0, contentInsetRight, contentInsetBottom);
+        assertContentFrame(w, new Rect(taskLeft, taskTop, taskRight - contentInsetRight,
+                taskBottom - contentInsetBottom));
 
         pf.set(0, 0, logicalWidth, logicalHeight);
         // However if we set temp inset bounds, the insets will be computed
@@ -314,15 +351,14 @@
         final int insetRight = insetLeft + (taskRight - taskLeft);
         final int insetBottom = insetTop + (taskBottom - taskTop);
         task.mInsetBounds.set(insetLeft, insetTop, insetRight, insetBottom);
-
         windowFrames.setFrames(pf, pf, pf, cf, cf, pf, cf, mEmptyRect);
         w.computeFrameLw(windowFrames);
-        assertRect(w.getFrameLw(), taskLeft, taskTop, taskRight, taskBottom);
+        assertFrame(w, taskLeft, taskTop, taskRight, taskBottom);
         contentInsetRight = insetRight - cfRight;
         contentInsetBottom = insetBottom - cfBottom;
-        assertRect(w.mContentInsets, 0, 0, contentInsetRight, contentInsetBottom);
-        assertRect(w.getContentFrameLw(), taskLeft, taskTop, taskRight - contentInsetRight,
-                taskBottom - contentInsetBottom);
+        assertContentInset(w, 0, 0, contentInsetRight, contentInsetBottom);
+        assertContentFrame(w, new Rect(taskLeft, taskTop, taskRight - contentInsetRight,
+                taskBottom - contentInsetBottom));
     }
 
     @Test
@@ -344,20 +380,16 @@
         final Rect vf = cf;
         final Rect sf = vf;
         // We use a decor content frame with insets to produce cropping.
-        Rect dcf = cf;
-
-        final Rect policyCrop = new Rect();
+        Rect dcf = new Rect(cf);
 
         final WindowFrames windowFrames = new WindowFrames(pf, df, of, cf, vf, dcf, sf, mEmptyRect);
         w.computeFrameLw(windowFrames);
-        w.calculatePolicyCrop(policyCrop);
-        assertRect(policyCrop, 0, cf.top, logicalWidth, cf.bottom);
+        assertPolicyCrop(w, 0, cf.top, logicalWidth, cf.bottom);
 
         windowFrames.mDecorFrame.setEmpty();
         // Likewise with no decor frame we would get no crop
         w.computeFrameLw(windowFrames);
-        w.calculatePolicyCrop(policyCrop);
-        assertRect(policyCrop, 0, 0, logicalWidth, logicalHeight);
+        assertPolicyCrop(w, 0, 0, logicalWidth, logicalHeight);
 
         // Now we set up a window which doesn't fill the entire decor frame.
         // Normally it would be cropped to it's frame but in the case of docked resizing
@@ -372,16 +404,14 @@
         w.mRequestedHeight = logicalHeight / 2;
         w.computeFrameLw(windowFrames);
 
-        w.calculatePolicyCrop(policyCrop);
         // Normally the crop is shrunk from the decor frame
         // to the computed window frame.
-        assertRect(policyCrop, 0, 0, logicalWidth / 2, logicalHeight / 2);
+        assertPolicyCrop(w, 0, 0, logicalWidth / 2, logicalHeight / 2);
 
         w.mDockedResizingForTest = true;
-        w.calculatePolicyCrop(policyCrop);
         // But if we are docked resizing it won't be, however we will still be
         // shrunk to the decor frame and the display.
-        assertRect(policyCrop, 0, 0,
+        assertPolicyCrop(w, 0, 0,
                 Math.min(pf.width(), displayInfo.logicalWidth),
                 Math.min(pf.height(), displayInfo.logicalHeight));
     }
@@ -409,9 +439,9 @@
         w.computeFrameLw(windowFrames);
         // For non fullscreen tasks the containing frame is based off the
         // task bounds not the parent frame.
-        assertRect(w.getFrameLw(), taskLeft, taskTop, taskRight, taskBottom);
-        assertRect(w.getContentFrameLw(), taskLeft, taskTop, taskRight, taskBottom);
-        assertRect(w.mContentInsets, 0, 0, 0, 0);
+        assertFrame(w, taskLeft, taskTop, taskRight, taskBottom);
+        assertContentFrame(w, taskBounds);
+        assertContentInset(w, 0, 0, 0, 0);
 
         // Now simulate switch to fullscreen for letterboxed app.
         final int xInset = logicalWidth / 10;
@@ -422,12 +452,11 @@
         w.mAppToken.onOverrideConfigurationChanged(config);
         pf.set(0, 0, logicalWidth, logicalHeight);
         task.mFullscreenForTest = true;
-
         windowFrames.setFrames(pf, pf, pf, cf, cf, pf, cf, mEmptyRect);
         w.computeFrameLw(windowFrames);
-        assertEquals(cf, w.getFrameLw());
-        assertEquals(cf, w.getContentFrameLw());
-        assertRect(w.mContentInsets, 0, 0, 0, 0);
+        assertFrame(w, cf.left, cf.top, cf.right, cf.bottom);
+        assertContentFrame(w, cf);
+        assertContentInset(w, 0, 0, 0, 0);
     }
 
     @Test
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java
index 742ad65..f255d49 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenerServiceTest.java
@@ -95,6 +95,7 @@
             assertEquals(getUserSentiment(i), ranking.getUserSentiment());
             assertEquals(getHidden(i), ranking.isSuspended());
             assertActionsEqual(getSmartActions(key, i), ranking.getSmartActions());
+            assertEquals(getSmartReplies(key, i), ranking.getSmartReplies());
         }
     }
 
@@ -112,6 +113,7 @@
         Bundle userSentiment = new Bundle();
         Bundle mHidden = new Bundle();
         Bundle smartActions = new Bundle();
+        Bundle smartReplies = new Bundle();
 
         for (int i = 0; i < mKeys.length; i++) {
             String key = mKeys[i];
@@ -130,12 +132,13 @@
             userSentiment.putInt(key, getUserSentiment(i));
             mHidden.putBoolean(key, getHidden(i));
             smartActions.putParcelableArrayList(key, getSmartActions(key, i));
+            smartReplies.putCharSequenceArrayList(key, getSmartReplies(key, i));
         }
         NotificationRankingUpdate update = new NotificationRankingUpdate(mKeys,
                 interceptedKeys.toArray(new String[0]), visibilityOverrides,
                 suppressedVisualEffects, importance, explanation, overrideGroupKeys,
                 channels, overridePeople, snoozeCriteria, showBadge, userSentiment, mHidden,
-                smartActions);
+                smartActions, smartReplies);
         return update;
     }
 
@@ -216,6 +219,14 @@
         return actions;
     }
 
+    private ArrayList<CharSequence> getSmartReplies(String key, int index) {
+        ArrayList<CharSequence> choices = new ArrayList<>();
+        for (int i = 0; i < index; i++) {
+            choices.add("choice_" + key + "_" + i);
+        }
+        return choices;
+    }
+
     private void assertActionsEqual(
             List<Notification.Action> expecteds, List<Notification.Action> actuals) {
         assertEquals(expecteds.size(), actuals.size());
diff --git a/telephony/java/com/android/ims/internal/uce/common/CapInfo.java b/telephony/java/com/android/ims/internal/uce/common/CapInfo.java
index 56969a8..a9847ba 100644
--- a/telephony/java/com/android/ims/internal/uce/common/CapInfo.java
+++ b/telephony/java/com/android/ims/internal/uce/common/CapInfo.java
@@ -16,6 +16,7 @@
 
 package com.android.ims.internal.uce.common;
 
+import android.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.Log;
@@ -73,6 +74,7 @@
     /**
      * Constructor for the CapInfo class.
      */
+    @UnsupportedAppUsage
     public CapInfo() {
     };
 
@@ -80,6 +82,7 @@
     /**
      * Checks whether IM is supported.
      */
+    @UnsupportedAppUsage
     public boolean isImSupported() {
         return mImSupported;
     }
@@ -87,6 +90,7 @@
     /**
      * Sets IM as supported or not supported.
      */
+    @UnsupportedAppUsage
     public void setImSupported(boolean imSupported) {
         this.mImSupported = imSupported;
     }
@@ -94,6 +98,7 @@
     /**
      * Checks whether FT Thumbnail is supported.
      */
+    @UnsupportedAppUsage
     public boolean isFtThumbSupported() {
         return mFtThumbSupported;
     }
@@ -101,6 +106,7 @@
     /**
      * Sets FT thumbnail as supported or not supported.
      */
+    @UnsupportedAppUsage
     public void setFtThumbSupported(boolean ftThumbSupported) {
         this.mFtThumbSupported = ftThumbSupported;
     }
@@ -110,6 +116,7 @@
     /**
      * Checks whether FT Store and Forward is supported
      */
+    @UnsupportedAppUsage
     public boolean isFtSnFSupported() {
         return  mFtSnFSupported;
     }
@@ -117,6 +124,7 @@
     /**
      * Sets FT Store and Forward as supported or not supported.
      */
+    @UnsupportedAppUsage
     public void setFtSnFSupported(boolean  ftSnFSupported) {
         this.mFtSnFSupported =  ftSnFSupported;
     }
@@ -124,6 +132,7 @@
    /**
     * Checks whether File transfer HTTP is supported.
     */
+   @UnsupportedAppUsage
    public boolean isFtHttpSupported() {
        return  mFtHttpSupported;
    }
@@ -131,6 +140,7 @@
    /**
     * Sets File transfer HTTP as supported or not supported.
     */
+   @UnsupportedAppUsage
    public void setFtHttpSupported(boolean  ftHttpSupported) {
        this.mFtHttpSupported =  ftHttpSupported;
    }
@@ -138,6 +148,7 @@
     /**
      * Checks whether FT is supported.
      */
+    @UnsupportedAppUsage
     public boolean isFtSupported() {
         return mFtSupported;
     }
@@ -145,6 +156,7 @@
     /**
      * Sets FT as supported or not supported.
      */
+    @UnsupportedAppUsage
     public void setFtSupported(boolean ftSupported) {
         this.mFtSupported = ftSupported;
     }
@@ -152,6 +164,7 @@
     /**
      * Checks whether IS is supported.
      */
+    @UnsupportedAppUsage
     public boolean isIsSupported() {
         return mIsSupported;
     }
@@ -159,6 +172,7 @@
     /**
      * Sets IS as supported or not supported.
      */
+    @UnsupportedAppUsage
     public void setIsSupported(boolean isSupported) {
         this.mIsSupported = isSupported;
     }
@@ -166,6 +180,7 @@
     /**
      * Checks whether video sharing is supported during a CS call.
      */
+    @UnsupportedAppUsage
     public boolean isVsDuringCSSupported() {
         return mVsDuringCSSupported;
     }
@@ -174,6 +189,7 @@
      *  Sets video sharing as supported or not supported during a CS
      *  call.
      */
+    @UnsupportedAppUsage
     public void setVsDuringCSSupported(boolean vsDuringCSSupported) {
         this.mVsDuringCSSupported = vsDuringCSSupported;
     }
@@ -182,6 +198,7 @@
      *  Checks whether video sharing outside a voice call is
      *   supported.
      */
+    @UnsupportedAppUsage
     public boolean isVsSupported() {
         return mVsSupported;
     }
@@ -189,6 +206,7 @@
     /**
      * Sets video sharing as supported or not supported.
      */
+    @UnsupportedAppUsage
     public void setVsSupported(boolean vsSupported) {
         this.mVsSupported = vsSupported;
     }
@@ -196,6 +214,7 @@
     /**
      * Checks whether social presence is supported.
      */
+    @UnsupportedAppUsage
     public boolean isSpSupported() {
         return mSpSupported;
     }
@@ -203,6 +222,7 @@
     /**
      * Sets social presence as supported or not supported.
      */
+    @UnsupportedAppUsage
     public void setSpSupported(boolean spSupported) {
         this.mSpSupported = spSupported;
     }
@@ -211,6 +231,7 @@
      * Checks whether capability discovery via presence is
      * supported.
      */
+    @UnsupportedAppUsage
     public boolean isCdViaPresenceSupported() {
         return mCdViaPresenceSupported;
     }
@@ -219,6 +240,7 @@
      * Sets capability discovery via presence as supported or not
      * supported.
      */
+    @UnsupportedAppUsage
     public void setCdViaPresenceSupported(boolean cdViaPresenceSupported) {
         this.mCdViaPresenceSupported = cdViaPresenceSupported;
     }
@@ -226,6 +248,7 @@
     /**
      * Checks whether IP voice call is supported.
      */
+    @UnsupportedAppUsage
     public boolean isIpVoiceSupported() {
         return mIpVoiceSupported;
     }
@@ -233,6 +256,7 @@
     /**
      * Sets IP voice call as supported or not supported.
      */
+    @UnsupportedAppUsage
     public void setIpVoiceSupported(boolean ipVoiceSupported) {
         this.mIpVoiceSupported = ipVoiceSupported;
     }
@@ -240,6 +264,7 @@
     /**
      * Checks whether IP video call is supported.
      */
+    @UnsupportedAppUsage
     public boolean isIpVideoSupported() {
         return mIpVideoSupported;
     }
@@ -247,6 +272,7 @@
     /**
      * Sets IP video call as supported or not supported.
      */
+    @UnsupportedAppUsage
     public void setIpVideoSupported(boolean ipVideoSupported) {
         this.mIpVideoSupported = ipVideoSupported;
     }
@@ -255,6 +281,7 @@
     * Checks whether Geo location Pull using File Transfer is
     * supported.
     */
+   @UnsupportedAppUsage
    public boolean isGeoPullFtSupported() {
        return mGeoPullFtSupported;
    }
@@ -263,6 +290,7 @@
     * Sets Geo location Pull using File Transfer as supported or
     * not supported.
     */
+   @UnsupportedAppUsage
    public void setGeoPullFtSupported(boolean geoPullFtSupported) {
        this.mGeoPullFtSupported = geoPullFtSupported;
    }
@@ -270,6 +298,7 @@
     /**
      * Checks whether Geo Pull is supported.
      */
+    @UnsupportedAppUsage
     public boolean isGeoPullSupported() {
         return mGeoPullSupported;
     }
@@ -277,6 +306,7 @@
     /**
      * Sets Geo Pull as supported or not supported.
      */
+    @UnsupportedAppUsage
     public void setGeoPullSupported(boolean geoPullSupported) {
         this.mGeoPullSupported = geoPullSupported;
     }
@@ -284,6 +314,7 @@
     /**
      * Checks whether Geo Push is supported.
      */
+    @UnsupportedAppUsage
     public boolean isGeoPushSupported() {
         return mGeoPushSupported;
     }
@@ -291,6 +322,7 @@
     /**
      * Sets Geo Push as supported or not supported.
      */
+    @UnsupportedAppUsage
     public void setGeoPushSupported(boolean geoPushSupported) {
         this.mGeoPushSupported = geoPushSupported;
     }
@@ -298,6 +330,7 @@
     /**
      * Checks whether short messaging is supported.
      */
+    @UnsupportedAppUsage
     public boolean isSmSupported() {
         return mSmSupported;
     }
@@ -305,6 +338,7 @@
     /**
      * Sets short messaging as supported or not supported.
      */
+    @UnsupportedAppUsage
     public void setSmSupported(boolean smSupported) {
         this.mSmSupported = smSupported;
     }
@@ -312,18 +346,22 @@
     /**
      * Checks whether store/forward and group chat are supported.
      */
+    @UnsupportedAppUsage
     public boolean isFullSnFGroupChatSupported() {
         return mFullSnFGroupChatSupported;
     }
 
+    @UnsupportedAppUsage
     public boolean isRcsIpVoiceCallSupported() {
         return mRcsIpVoiceCallSupported;
     }
 
+    @UnsupportedAppUsage
     public boolean isRcsIpVideoCallSupported() {
         return mRcsIpVideoCallSupported;
     }
 
+    @UnsupportedAppUsage
     public boolean isRcsIpVideoOnlyCallSupported() {
         return mRcsIpVideoOnlyCallSupported;
     }
@@ -331,16 +369,20 @@
     /**
      * Sets store/forward and group chat supported or not supported.
      */
+    @UnsupportedAppUsage
     public void setFullSnFGroupChatSupported(boolean fullSnFGroupChatSupported) {
         this.mFullSnFGroupChatSupported = fullSnFGroupChatSupported;
     }
 
+    @UnsupportedAppUsage
     public void setRcsIpVoiceCallSupported(boolean rcsIpVoiceCallSupported) {
         this.mRcsIpVoiceCallSupported = rcsIpVoiceCallSupported;
     }
+    @UnsupportedAppUsage
     public void setRcsIpVideoCallSupported(boolean rcsIpVideoCallSupported) {
         this.mRcsIpVideoCallSupported = rcsIpVideoCallSupported;
     }
+    @UnsupportedAppUsage
     public void setRcsIpVideoOnlyCallSupported(boolean rcsIpVideoOnlyCallSupported) {
         this.mRcsIpVideoOnlyCallSupported = rcsIpVideoOnlyCallSupported;
     }
@@ -351,17 +393,20 @@
     }
 
     /** Sets the list of supported extensions. */
+    @UnsupportedAppUsage
     public void setExts(String[] exts) {
         this.mExts = exts;
     }
 
 
     /** Gets the time stamp for when to query again. */
+    @UnsupportedAppUsage
     public long getCapTimestamp() {
         return mCapTimestamp;
     }
 
     /** Sets the time stamp for when to query again. */
+    @UnsupportedAppUsage
     public void setCapTimestamp(long capTimestamp) {
         this.mCapTimestamp = capTimestamp;
     }
diff --git a/telephony/java/com/android/ims/internal/uce/common/StatusCode.java b/telephony/java/com/android/ims/internal/uce/common/StatusCode.java
index ad9b669..3921cfb 100644
--- a/telephony/java/com/android/ims/internal/uce/common/StatusCode.java
+++ b/telephony/java/com/android/ims/internal/uce/common/StatusCode.java
@@ -16,6 +16,7 @@
 
 package com.android.ims.internal.uce.common;
 
+import android.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.Log;
@@ -71,12 +72,14 @@
      * Constructor for the StatusCode class.
      * @hide
      */
+    @UnsupportedAppUsage
     public StatusCode() {}
 
     /**
      *  Gets the status code.
      *  @hide
      */
+    @UnsupportedAppUsage
     public int getStatusCode() {
         return mStatusCode;
     }
@@ -85,6 +88,7 @@
      *  Sets the status code.
      *  @hide
      */
+    @UnsupportedAppUsage
     public void setStatusCode(int nStatusCode) {
         this.mStatusCode = nStatusCode;
     }
diff --git a/telephony/java/com/android/ims/internal/uce/common/UceLong.java b/telephony/java/com/android/ims/internal/uce/common/UceLong.java
index fd07fe8..7207899 100644
--- a/telephony/java/com/android/ims/internal/uce/common/UceLong.java
+++ b/telephony/java/com/android/ims/internal/uce/common/UceLong.java
@@ -16,6 +16,7 @@
 
 package com.android.ims.internal.uce.common;
 
+import android.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.Log;
@@ -32,6 +33,7 @@
      * Constructor for the UceLong class.
      * @hide
      */
+    @UnsupportedAppUsage
     public UceLong() {
     };
 
@@ -39,6 +41,7 @@
      * Gets the long value.
      * @hide
      */
+    @UnsupportedAppUsage
     public long getUceLong() {
         return mUceLong;
     }
@@ -47,6 +50,7 @@
      * Sets the long value.
      * @hide
      */
+    @UnsupportedAppUsage
     public void setUceLong(long uceLong) {
         this.mUceLong = uceLong;
     }
@@ -54,6 +58,7 @@
     /** Get the client ID as integer value.
      *  @hide
      */
+    @UnsupportedAppUsage
     public int getClientId() {
         return mClientId;
     }
@@ -62,6 +67,7 @@
      * Set the client ID as integer value.
      * @hide
      */
+    @UnsupportedAppUsage
     public void setClientId(int nClientId) {
         this.mClientId = nClientId;
     }
diff --git a/telephony/java/com/android/ims/internal/uce/options/OptionsCapInfo.java b/telephony/java/com/android/ims/internal/uce/options/OptionsCapInfo.java
index c570f49..bcb9f2d 100644
--- a/telephony/java/com/android/ims/internal/uce/options/OptionsCapInfo.java
+++ b/telephony/java/com/android/ims/internal/uce/options/OptionsCapInfo.java
@@ -15,6 +15,7 @@
  */
 package com.android.ims.internal.uce.options;
 
+import android.annotation.UnsupportedAppUsage;
 import com.android.ims.internal.uce.common.CapInfo;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -30,10 +31,12 @@
         return new OptionsCapInfo();
     }
 
+    @UnsupportedAppUsage
     public String getSdp() {
         return mSdp;
     }
 
+    @UnsupportedAppUsage
     public void setSdp(String sdp) {
         this.mSdp = sdp;
     }
@@ -41,16 +44,19 @@
     /**
      * Constructor for the OptionsCapInfo class.
      */
+    @UnsupportedAppUsage
     public OptionsCapInfo() {
         mCapInfo = new CapInfo();
     };
 
+    @UnsupportedAppUsage
     public CapInfo getCapInfo() {
         return mCapInfo;
     }
     /**
      * Sets the CapInfo
      */
+    @UnsupportedAppUsage
     public void setCapInfo(CapInfo capInfo) {
         this.mCapInfo = capInfo;
     }
diff --git a/telephony/java/com/android/ims/internal/uce/options/OptionsCmdId.java b/telephony/java/com/android/ims/internal/uce/options/OptionsCmdId.java
index 35f769c..14c64ac 100644
--- a/telephony/java/com/android/ims/internal/uce/options/OptionsCmdId.java
+++ b/telephony/java/com/android/ims/internal/uce/options/OptionsCmdId.java
@@ -17,6 +17,7 @@
 package com.android.ims.internal.uce.options;
 
 
+import android.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -55,6 +56,7 @@
      * Sets the command ID.
      * @hide
      */
+    @UnsupportedAppUsage
     public void setCmdId(int nCmdId) {
         this.mCmdId = nCmdId;
     }
@@ -63,6 +65,7 @@
      * Constructor for the OptionsCDCmdId class.
      * @hide
      */
+    @UnsupportedAppUsage
     public OptionsCmdId(){};
 
     /** @hide */
diff --git a/telephony/java/com/android/ims/internal/uce/options/OptionsCmdStatus.java b/telephony/java/com/android/ims/internal/uce/options/OptionsCmdStatus.java
index dab191c..4af3e6e 100644
--- a/telephony/java/com/android/ims/internal/uce/options/OptionsCmdStatus.java
+++ b/telephony/java/com/android/ims/internal/uce/options/OptionsCmdStatus.java
@@ -19,6 +19,7 @@
 import com.android.ims.internal.uce.common.StatusCode;
 import com.android.ims.internal.uce.common.CapInfo;
 
+import android.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -41,6 +42,7 @@
      * Sets the command ID.
      * @hide
      */
+    @UnsupportedAppUsage
     public void setCmdId(OptionsCmdId cmdId) {
         this.mCmdId = cmdId;
     }
@@ -56,6 +58,7 @@
     /**
        Sets the user data.
        @hide  */
+    @UnsupportedAppUsage
     public void setUserData(int userData) {
         this.mUserData = userData;
     }
@@ -72,6 +75,7 @@
      * Sets the status code.
      * @hide
      */
+    @UnsupportedAppUsage
     public void setStatus(StatusCode status) {
         this.mStatus = status;
     }
@@ -80,6 +84,7 @@
      * Constructor for the OptionsCmdStatus class.
      * @hide
      */
+    @UnsupportedAppUsage
     public OptionsCmdStatus() {
         mStatus = new StatusCode();
         mCapInfo = new CapInfo();
@@ -96,6 +101,7 @@
      * Sets the CapInfo
      * @hide
      */
+    @UnsupportedAppUsage
     public void setCapInfo(CapInfo capInfo) {
         this.mCapInfo = capInfo;
     }
diff --git a/telephony/java/com/android/ims/internal/uce/options/OptionsSipResponse.java b/telephony/java/com/android/ims/internal/uce/options/OptionsSipResponse.java
index 0b9dd21..c5f333d 100644
--- a/telephony/java/com/android/ims/internal/uce/options/OptionsSipResponse.java
+++ b/telephony/java/com/android/ims/internal/uce/options/OptionsSipResponse.java
@@ -16,6 +16,7 @@
 
 package com.android.ims.internal.uce.options;
 
+import android.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -41,6 +42,7 @@
      * Sets the Options command ID.
      * @hide
      */
+    @UnsupportedAppUsage
     public void setCmdId(OptionsCmdId cmdId) {
         this.mCmdId = cmdId;
     }
@@ -57,6 +59,7 @@
      * Sets the request ID
      * @hide
      */
+    @UnsupportedAppUsage
     public void setRequestId(int requestId) {
         this.mRequestId = requestId;
     }
@@ -73,6 +76,7 @@
      * Sets the SIP response code.
      * @hide
      */
+    @UnsupportedAppUsage
     public void setSipResponseCode(int sipResponseCode) {
         this.mSipResponseCode = sipResponseCode;
     }
@@ -89,6 +93,7 @@
      * Sets the SIP response code reason phrase.
      * @hide
      */
+    @UnsupportedAppUsage
     public void setReasonPhrase(String reasonPhrase) {
         this.mReasonPhrase = reasonPhrase;
     }
@@ -105,6 +110,7 @@
      * Sets the SIP retryAfter sec value
      * @hide
      */
+    @UnsupportedAppUsage
     public void setRetryAfter(int retryAfter) {
         this.mRetryAfter = retryAfter;
     }
@@ -113,6 +119,7 @@
      * Constructor for the OptionsSipResponse class.
      * @hide
      */
+    @UnsupportedAppUsage
     public OptionsSipResponse() {
         mCmdId = new OptionsCmdId();
     };
diff --git a/telephony/java/com/android/ims/internal/uce/presence/PresCapInfo.java b/telephony/java/com/android/ims/internal/uce/presence/PresCapInfo.java
index 60fc226..745df5b 100644
--- a/telephony/java/com/android/ims/internal/uce/presence/PresCapInfo.java
+++ b/telephony/java/com/android/ims/internal/uce/presence/PresCapInfo.java
@@ -18,6 +18,7 @@
 
 import com.android.ims.internal.uce.common.CapInfo;
 
+import android.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -26,12 +27,14 @@
 public class PresCapInfo implements Parcelable {
 
     private CapInfo mCapInfo;
+    @UnsupportedAppUsage
     private String mContactUri = "";
 
     /**
      * Gets the UCE capability information.
      * @hide
      */
+    @UnsupportedAppUsage
     public CapInfo getCapInfo() {
         return mCapInfo;
     }
@@ -48,6 +51,7 @@
      *  Gets the contact URI.
      *  @hide
      */
+    @UnsupportedAppUsage
     public String getContactUri() {
         return mContactUri;
     }
diff --git a/telephony/java/com/android/ims/internal/uce/presence/PresCmdId.java b/telephony/java/com/android/ims/internal/uce/presence/PresCmdId.java
index 395f3e8..41020ec 100644
--- a/telephony/java/com/android/ims/internal/uce/presence/PresCmdId.java
+++ b/telephony/java/com/android/ims/internal/uce/presence/PresCmdId.java
@@ -16,6 +16,7 @@
 
 package com.android.ims.internal.uce.presence;
 
+import android.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -57,6 +58,7 @@
      * Sets the command ID.
      * @hide
      */
+    @UnsupportedAppUsage
     public void setCmdId(int nCmdId) {
         this.mCmdId = nCmdId;
     }
@@ -66,6 +68,7 @@
     * Constructor for the PresCmdId class.
     * @hide
     */
+    @UnsupportedAppUsage
     public PresCmdId(){};
 
 
diff --git a/telephony/java/com/android/ims/internal/uce/presence/PresCmdStatus.java b/telephony/java/com/android/ims/internal/uce/presence/PresCmdStatus.java
index a5b498b..ff8069c 100644
--- a/telephony/java/com/android/ims/internal/uce/presence/PresCmdStatus.java
+++ b/telephony/java/com/android/ims/internal/uce/presence/PresCmdStatus.java
@@ -18,6 +18,7 @@
 
 import com.android.ims.internal.uce.common.StatusCode;
 
+import android.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -42,6 +43,7 @@
      * Sets the command ID.
      * @hide
      */
+    @UnsupportedAppUsage
     public void setCmdId(PresCmdId cmdId) {
         this.mCmdId = cmdId;
     }
@@ -58,6 +60,7 @@
      * Sets the user data.
      * @hide
      */
+    @UnsupportedAppUsage
     public void setUserData(int userData) {
         this.mUserData = userData;
     }
@@ -73,6 +76,7 @@
      * Sets the status code.
      * @hide
      */
+    @UnsupportedAppUsage
     public void setStatus(StatusCode status) {
         this.mStatus = status;
     }
@@ -89,6 +93,7 @@
      * Sets the request ID.
      * @hide
      */
+    @UnsupportedAppUsage
     public void setRequestId(int requestId) {
         this.mRequestId = requestId;
     }
@@ -97,6 +102,7 @@
      * Constructor for the PresCmdStatus class.
      * @hide
      */
+    @UnsupportedAppUsage
     public PresCmdStatus() {
         mStatus = new StatusCode();
     };
diff --git a/telephony/java/com/android/ims/internal/uce/presence/PresPublishTriggerType.java b/telephony/java/com/android/ims/internal/uce/presence/PresPublishTriggerType.java
index 3e8531a..87193e3 100644
--- a/telephony/java/com/android/ims/internal/uce/presence/PresPublishTriggerType.java
+++ b/telephony/java/com/android/ims/internal/uce/presence/PresPublishTriggerType.java
@@ -16,6 +16,7 @@
 
 package com.android.ims.internal.uce.presence;
 
+import android.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -65,6 +66,7 @@
      * Sets the publish trigger type.
      * @hide
      */
+    @UnsupportedAppUsage
     public void setPublishTrigeerType(int nPublishTriggerType) {
         this.mPublishTriggerType = nPublishTriggerType;
     }
@@ -74,6 +76,7 @@
      * Constructor for the PresPublishTriggerType class.
      * @hide
      */
+    @UnsupportedAppUsage
     public PresPublishTriggerType(){};
 
     /** @hide */
diff --git a/telephony/java/com/android/ims/internal/uce/presence/PresResInfo.java b/telephony/java/com/android/ims/internal/uce/presence/PresResInfo.java
index a073a23..237c999 100644
--- a/telephony/java/com/android/ims/internal/uce/presence/PresResInfo.java
+++ b/telephony/java/com/android/ims/internal/uce/presence/PresResInfo.java
@@ -16,6 +16,7 @@
 
 package com.android.ims.internal.uce.presence;
 
+import android.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -38,6 +39,7 @@
      * Sets the Presence service resource instance information.
      * @hide
      */
+    @UnsupportedAppUsage
     public void setInstanceInfo(PresResInstanceInfo instanceInfo) {
         this.mInstanceInfo = instanceInfo;
     }
@@ -54,6 +56,7 @@
      * Sets the resource URI.
      * @hide
      */
+    @UnsupportedAppUsage
     public void setResUri(String resUri) {
         this.mResUri = resUri;
     }
@@ -70,6 +73,7 @@
      * Sets the display name.
      * @hide
      */
+    @UnsupportedAppUsage
     public void setDisplayName(String displayName) {
         this.mDisplayName = displayName;
     }
@@ -79,6 +83,7 @@
     * Constructor for the PresResInstanceInfo class.
     * @hide
     */
+    @UnsupportedAppUsage
     public PresResInfo() {
         mInstanceInfo = new PresResInstanceInfo();
     };
diff --git a/telephony/java/com/android/ims/internal/uce/presence/PresResInstanceInfo.java b/telephony/java/com/android/ims/internal/uce/presence/PresResInstanceInfo.java
index 430cff1..29699ea 100644
--- a/telephony/java/com/android/ims/internal/uce/presence/PresResInstanceInfo.java
+++ b/telephony/java/com/android/ims/internal/uce/presence/PresResInstanceInfo.java
@@ -16,6 +16,7 @@
 
 package com.android.ims.internal.uce.presence;
 
+import android.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 import java.util.Arrays;
@@ -59,6 +60,7 @@
      * Sets the resource instance state.
      * @hide
      */
+    @UnsupportedAppUsage
     public void setResInstanceState(int nResInstanceState) {
         this.mResInstanceState = nResInstanceState;
     }
@@ -75,6 +77,7 @@
      * Sets the resource ID.
      * @hide
      */
+    @UnsupportedAppUsage
     public void setResId(String resourceId) {
         this.mId = resourceId;
     }
@@ -93,6 +96,7 @@
      * code.
      * @hide
      */
+    @UnsupportedAppUsage
     public void setReason(String reason) {
         this.mReason = reason;
     }
@@ -109,6 +113,7 @@
      * Sets the entity URI.
      * @hide
      */
+    @UnsupportedAppUsage
     public void setPresentityUri(String presentityUri) {
         this.mPresentityUri = presentityUri;
     }
@@ -125,6 +130,7 @@
      * Sets the tuple information.
      * @hide
      */
+    @UnsupportedAppUsage
     public void setTupleInfo(PresTupleInfo[] tupleInfo) {
         this.mTupleInfoArray = new PresTupleInfo[tupleInfo.length];
         this.mTupleInfoArray = tupleInfo;
@@ -135,6 +141,7 @@
     * Constructor for the PresResInstanceInfo class.
     * @hide
     */
+    @UnsupportedAppUsage
     public PresResInstanceInfo(){
 
     };
diff --git a/telephony/java/com/android/ims/internal/uce/presence/PresRlmiInfo.java b/telephony/java/com/android/ims/internal/uce/presence/PresRlmiInfo.java
index 987dd77..ab46e4b 100644
--- a/telephony/java/com/android/ims/internal/uce/presence/PresRlmiInfo.java
+++ b/telephony/java/com/android/ims/internal/uce/presence/PresRlmiInfo.java
@@ -16,6 +16,7 @@
 
 package com.android.ims.internal.uce.presence;
 
+import android.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -60,6 +61,7 @@
      * Sets the URI.
      * @hide
      */
+    @UnsupportedAppUsage
     public void setUri(String uri) {
         this.mUri = uri;
     }
@@ -76,6 +78,7 @@
      * Sets the version.
      * @hide
      */
+    @UnsupportedAppUsage
     public void setVersion(int version) {
         this.mVersion = version;
     }
@@ -92,6 +95,7 @@
      * Sets the RLMI state.
      * @hide
      */
+    @UnsupportedAppUsage
     public void setFullState(boolean fullState) {
         this.mFullState = fullState;
     }
@@ -108,6 +112,7 @@
      * Sets the RLMI list name.
      * @hide
      */
+    @UnsupportedAppUsage
     public void setListName(String listName) {
         this.mListName = listName;
     }
@@ -124,6 +129,7 @@
      * Sets the subscription request ID.
      * @hide
      */
+    @UnsupportedAppUsage
     public void setRequestId(int requestId) {
         this.mRequestId = requestId;
     }
@@ -140,6 +146,7 @@
      * Sets the presence subscription state.
      * @hide
      */
+    @UnsupportedAppUsage
     public void setPresSubscriptionState(PresSubscriptionState presSubscriptionState) {
         this.mPresSubscriptionState = presSubscriptionState;
     }
@@ -156,6 +163,7 @@
      * Sets the presence subscription expiration time.
      * @hide
      */
+    @UnsupportedAppUsage
     public void setSubscriptionExpireTime(int subscriptionExpireTime) {
         this.mSubscriptionExpireTime = subscriptionExpireTime;
     }
@@ -172,6 +180,7 @@
      * Sets the presence subscription terminated reason.
      * @hide
      */
+    @UnsupportedAppUsage
     public void setSubscriptionTerminatedReason(String subscriptionTerminatedReason) {
         this.mSubscriptionTerminatedReason = subscriptionTerminatedReason;
     }
@@ -180,6 +189,7 @@
      * Constructor for the PresTupleInfo class.
      * @hide
      */
+    @UnsupportedAppUsage
     public PresRlmiInfo(){};
 
     /** @hide */
diff --git a/telephony/java/com/android/ims/internal/uce/presence/PresServiceInfo.java b/telephony/java/com/android/ims/internal/uce/presence/PresServiceInfo.java
index f7b7264..83ba722 100644
--- a/telephony/java/com/android/ims/internal/uce/presence/PresServiceInfo.java
+++ b/telephony/java/com/android/ims/internal/uce/presence/PresServiceInfo.java
@@ -16,6 +16,7 @@
 
 package com.android.ims.internal.uce.presence;
 
+import android.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -45,6 +46,7 @@
      * Gets the media type.
      * @hide
      */
+    @UnsupportedAppUsage
     public int getMediaType() {
         return mMediaCap;
     }
@@ -61,6 +63,7 @@
      * Gets the service ID.
      * @hide
      */
+    @UnsupportedAppUsage
     public String getServiceId() {
         return mServiceID;
     }
@@ -76,6 +79,7 @@
      * Gets the service description.
      * @hide
      */
+    @UnsupportedAppUsage
     public String getServiceDesc() {
         return mServiceDesc;
     }
@@ -92,6 +96,7 @@
      * Gets the service version.
      * @hide
      */
+    @UnsupportedAppUsage
     public String getServiceVer() {
         return mServiceVer;
     }
diff --git a/telephony/java/com/android/ims/internal/uce/presence/PresSipResponse.java b/telephony/java/com/android/ims/internal/uce/presence/PresSipResponse.java
index 456b443..5e42592 100644
--- a/telephony/java/com/android/ims/internal/uce/presence/PresSipResponse.java
+++ b/telephony/java/com/android/ims/internal/uce/presence/PresSipResponse.java
@@ -16,6 +16,7 @@
 
 package com.android.ims.internal.uce.presence;
 
+import android.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -32,6 +33,7 @@
      * Gets the Presence command ID.
      * @hide
      */
+    @UnsupportedAppUsage
     public PresCmdId getCmdId() {
         return mCmdId;
     }
@@ -40,6 +42,7 @@
      * Sets the Presence command ID.
      * @hide
      */
+    @UnsupportedAppUsage
     public void setCmdId(PresCmdId cmdId) {
         this.mCmdId = cmdId;
     }
@@ -48,6 +51,7 @@
      * Gets the request ID.
      * @hide
      */
+    @UnsupportedAppUsage
     public int getRequestId() {
         return mRequestId;
     }
@@ -56,6 +60,7 @@
      * Sets the request ID.
      * @hide
      */
+    @UnsupportedAppUsage
     public void setRequestId(int requestId) {
         this.mRequestId = requestId;
     }
@@ -64,6 +69,7 @@
      * Gets the SIP response code.
      * @hide
      */
+    @UnsupportedAppUsage
     public int getSipResponseCode() {
         return mSipResponseCode;
     }
@@ -72,6 +78,7 @@
      * Sets the SIP response code.
      * @hide
      */
+    @UnsupportedAppUsage
     public void setSipResponseCode(int sipResponseCode) {
         this.mSipResponseCode = sipResponseCode;
     }
@@ -82,6 +89,7 @@
      * code.
      * @hide
      */
+    @UnsupportedAppUsage
     public String getReasonPhrase() {
         return mReasonPhrase;
     }
@@ -90,6 +98,7 @@
      * Sets the SIP response code reason phrase.
      * @hide
      */
+    @UnsupportedAppUsage
     public void setReasonPhrase(String reasonPhrase) {
         this.mReasonPhrase = reasonPhrase;
     }
@@ -98,6 +107,7 @@
      * Gets the SIP retryAfter sec value.
      * @hide
      */
+    @UnsupportedAppUsage
     public int getRetryAfter() {
         return mRetryAfter;
     }
@@ -106,6 +116,7 @@
      * Sets the SIP retryAfter sec value
      * @hide
      */
+    @UnsupportedAppUsage
     public void setRetryAfter(int retryAfter) {
         this.mRetryAfter = retryAfter;
     }
@@ -114,6 +125,7 @@
      * Constructor for the PresSipResponse class.
      * @hide
      */
+    @UnsupportedAppUsage
     public PresSipResponse(){};
 
     /** @hide */
diff --git a/telephony/java/com/android/ims/internal/uce/presence/PresSubscriptionState.java b/telephony/java/com/android/ims/internal/uce/presence/PresSubscriptionState.java
index 872bc23..bee928c 100644
--- a/telephony/java/com/android/ims/internal/uce/presence/PresSubscriptionState.java
+++ b/telephony/java/com/android/ims/internal/uce/presence/PresSubscriptionState.java
@@ -16,6 +16,7 @@
 
 package com.android.ims.internal.uce.presence;
 
+import android.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -77,6 +78,7 @@
      * Constructor for the PresSubscriptionState class.
      * @hide
      */
+    @UnsupportedAppUsage
     public PresSubscriptionState() {    };
 
     /**
@@ -92,6 +94,7 @@
      * Sets the Presence subscription state.
      * @hide
      */
+    @UnsupportedAppUsage
     public void setPresSubscriptionState(int nPresSubscriptionState) {
         this.mPresSubscriptionState = nPresSubscriptionState;
     }
diff --git a/telephony/java/com/android/ims/internal/uce/presence/PresTupleInfo.java b/telephony/java/com/android/ims/internal/uce/presence/PresTupleInfo.java
index e1867c5..7a47786 100644
--- a/telephony/java/com/android/ims/internal/uce/presence/PresTupleInfo.java
+++ b/telephony/java/com/android/ims/internal/uce/presence/PresTupleInfo.java
@@ -16,6 +16,7 @@
 
 package com.android.ims.internal.uce.presence;
 
+import android.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -39,6 +40,7 @@
      * Sets the feature tag.
      * @hide
      */
+    @UnsupportedAppUsage
     public void setFeatureTag(String featureTag) {
         this.mFeatureTag = featureTag;
     }
@@ -54,6 +56,7 @@
      * Sets the contact URI.
      * @hide
      */
+    @UnsupportedAppUsage
     public void setContactUri(String contactUri) {
         this.mContactUri = contactUri;
     }
@@ -70,6 +73,7 @@
      * Sets the timestamp.
      * @hide
      */
+    @UnsupportedAppUsage
     public void setTimestamp(String timestamp) {
         this.mTimestamp = timestamp;
     }
@@ -78,6 +82,7 @@
      * Constructor for the PresTupleInfo class.
      * @hide
      */
+    @UnsupportedAppUsage
     public PresTupleInfo(){};
 
     /** @hide */
diff --git a/tests/FlickerTests/Android.mk b/tests/FlickerTests/Android.mk
new file mode 100644
index 0000000..3c70f8b
--- /dev/null
+++ b/tests/FlickerTests/Android.mk
@@ -0,0 +1,35 @@
+#
+# Copyright (C) 2018 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.
+#
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_PACKAGE_NAME := FlickerTests
+LOCAL_MODULE_TAGS := tests optional
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_PRIVATE_PLATFORM_APIS := true
+LOCAL_CERTIFICATE := platform
+LOCAL_COMPATIBILITY_SUITE := device-tests
+
+LOCAL_JAVA_LIBRARIES := android.test.runner
+LOCAL_STATIC_JAVA_LIBRARIES := \
+    flickertestapplib \
+    flickerlib \
+    truth-prebuilt \
+    app-helpers-core
+
+include $(BUILD_PACKAGE)
+include $(call all-makefiles-under,$(LOCAL_PATH))
\ No newline at end of file
diff --git a/tests/FlickerTests/AndroidManifest.xml b/tests/FlickerTests/AndroidManifest.xml
new file mode 100644
index 0000000..ba63940
--- /dev/null
+++ b/tests/FlickerTests/AndroidManifest.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="com.android.server.wm.flicker">
+
+    <uses-sdk android:minSdkVersion="27" android:targetSdkVersion="27"/>
+    <!-- Read and write traces from external storage -->
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+    <!-- Capture screen contents -->
+    <uses-permission android:name="android.permission.ACCESS_SURFACE_FLINGER" />
+    <!-- Run layers trace -->
+    <uses-permission android:name="android.permission.HARDWARE_TEST"/>
+    <application>
+        <uses-library android:name="android.test.runner"/>
+    </application>
+
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+                     android:targetPackage="com.android.server.wm.flicker"
+                     android:label="WindowManager Flicker Tests">
+    </instrumentation>
+</manifest>
\ No newline at end of file
diff --git a/tests/FlickerTests/AndroidTest.xml b/tests/FlickerTests/AndroidTest.xml
new file mode 100644
index 0000000..b31235b
--- /dev/null
+++ b/tests/FlickerTests/AndroidTest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright 2018 Google Inc. All Rights Reserved.
+ -->
+<configuration description="Runs WindowManager Flicker Tests">
+    <option name="test-tag" value="FlickerTests" />
+    <target_preparer class="com.google.android.tradefed.targetprep.GoogleDeviceSetup">
+        <!-- keeps the screen on during tests -->
+        <option name="screen-always-on" value="on" />
+        <!-- prevents the phone from restarting -->
+        <option name="force-skip-system-props" value="true" />
+    </target_preparer>
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+        <option name="cleanup-apks" value="true"/>
+        <option name="test-file-name" value="FlickerTests.apk"/>
+        <option name="test-file-name" value="FlickerTestApp.apk" />
+    </target_preparer>
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+        <option name="package" value="com.android.server.wm.flicker"/>
+        <option name="shell-timeout" value="6600s" />
+        <option name="test-timeout" value="6000s" />
+        <option name="hidden-api-checks" value="false" />
+    </test>
+    <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
+        <option name="directory-keys" value="/sdcard/flicker" />
+        <option name="collect-on-run-ended-only" value="true" />
+    </metrics_collector>
+</configuration>
diff --git a/tests/FlickerTests/README.md b/tests/FlickerTests/README.md
new file mode 100644
index 0000000..a7c9e20
--- /dev/null
+++ b/tests/FlickerTests/README.md
@@ -0,0 +1,146 @@
+# Flicker Test Library
+
+## Motivation
+Detect *flicker* &mdash; any discontinuous, or unpredictable behavior seen during UI transitions that is not due to performance. This is often the result of a logic error in the code and difficult to identify because the issue is transient and at times difficult to reproduce. This library helps create integration tests between `SurfaceFlinger`, `WindowManager` and `SystemUI` to identify flicker.
+
+## Adding a Test
+The library builds and runs UI transitions, captures Winscope traces and exposes common assertions that can be tested against each trace.
+
+### Building Transitions
+Start by defining common or error prone transitions using `TransitionRunner`.
+```java
+// Example: Build a transition that cold launches an app from launcher
+TransitionRunner transition = TransitionRunner.newBuilder()
+                // Specify a tag to identify the transition (optional)
+                .withTag("OpenAppCold_" + testApp.getLauncherName())
+
+                // Specify preconditions to setup the device
+                // Wake up device and go to home screen
+                .runBeforeAll(AutomationUtils::wakeUpAndGoToHomeScreen)
+
+                // Setup transition under test
+                // Press the home button and close the app to test a cold start
+                .runBefore(device::pressHome)
+                .runBefore(testApp::exit)
+
+                // Run the transition under test
+                // Open the app and wait for UI to be idle
+                // This is the part of the transition that will be tested.
+                .run(testApp::open)
+                .run(device::waitForIdle)
+
+                // Perform any tear downs
+                // Close the app
+                .runAfterAll(testApp::exit)
+
+                // Number of times to repeat the transition to catch any flaky issues
+                .repeat(5);
+```
+
+
+Run the transition to get a list of `TransitionResult` for each time the transition is repeated.
+```java
+    List<TransitionResult> results = transition.run();
+```
+`TransitionResult` contains paths to test artifacts such as Winscope traces and screen recordings.
+
+
+### Checking Assertions
+Each `TransitionResult` can be tested using an extension of the Google Truth library, `LayersTraceSubject` and `WmTraceSubject`. They try to balance test principles set out by Google Truth (not supporting nested assertions, keeping assertions simple) with providing support for common assertion use cases.
+
+Each trace can be represented as a ordered collection of trace entries, with an associated timestamp. Each trace entry has common assertion checks. The trace subjects expose methods to filter the range of entries and test for changing assertions.
+
+```java
+    TransitionResult result = results.get(0);
+    Rect displayBounds = getDisplayBounds();
+
+    // check all trace entries
+    assertThat(result).coversRegion(displayBounds).forAllEntries();
+
+    // check a range of entries
+    assertThat(result).coversRegion(displayBounds).forRange(startTime, endTime);
+
+    // check first entry
+    assertThat(result).coversRegion(displayBounds).inTheBeginning();
+
+    // check last entry
+    assertThat(result).coversRegion(displayBounds).atTheEnd();
+
+    // check a change in assertions, e.g. wallpaper window is visible,
+    // then wallpaper window becomes and stays invisible
+    assertThat(result)
+                .showsBelowAppWindow("wallpaper")
+                .then()
+                .hidesBelowAppWindow("wallpaper")
+                .forAllEntries();
+```
+
+All assertions return `Result` which contains a `success` flag, `assertionName` string identifier, and `reason` string to provide actionable details to the user. The `reason` string is build along the way with all the details as to why the assertions failed and any hints which might help the user determine the root cause. Failed assertion message will also contain a path to the trace that was tested. Example of a failed test:
+
+```
+    java.lang.AssertionError: Not true that <com.android.server.wm.flicker.LayersTrace@65da4cc>
+    Layers Trace can be found in: /layers_trace_emptyregion.pb
+    Timestamp: 2308008331271
+    Assertion: coversRegion
+    Reason:   Region to test: Rect(0, 0 - 1440, 2880)
+    first empty point: 0, 99
+    visible regions:
+    StatusBar#0Rect(0, 0 - 1440, 98)
+    NavigationBar#0Rect(0, 2712 - 1440, 2880)
+    ScreenDecorOverlay#0Rect(0, 0 - 1440, 91)
+    ...
+        at com.google.common.truth.FailureStrategy.fail(FailureStrategy.java:24)
+        ...
+```
+
+---
+
+## Running Tests
+
+The tests can be run as any other Android JUnit tests. `platform_testing/tests/flicker` uses the library to test common UI transitions. Run `atest FlickerTest` to execute these tests.
+
+---
+
+## Other Topics
+### Monitors
+Monitors capture test artifacts for each transition run. They are started before each iteration of the test transition (after the `runBefore` calls) and stopped after the transition is completed. Each iteration will produce a new test artifact. The following monitors are available:
+
+#### LayersTraceMonitor
+Captures Layers trace. This monitor is started by default. Build a transition with `skipLayersTrace()` to disable this monitor.
+#### WindowManagerTraceMonitor
+Captures Window Manager trace. This monitor is started by default. Build a transition with `skipWindowManagerTrace()` to disable this monitor.
+#### WindowAnimationFrameStatsMonitor
+Captures WindowAnimationFrameStats for the transition. This monitor is started by default and is used to eliminate *janky* runs. If an iteration has skipped frames, as determined by WindowAnimationFrameStats, the results for the iteration is skipped. If the list of results is empty after all iterations are completed, then the test should fail. Build a transition with `includeJankyRuns()` to disable this monitor.
+#### ScreenRecorder
+Captures screen to a video file. This monitor is disabled by default. Build a transition with `recordEachRun()` to capture each transition or build with `recordAllRuns()` to capture every transition including setup and teardown.
+
+---
+
+### Extending Assertions
+
+To add a new assertion, add a function to one of the trace entry classes, `LayersTrace.Entry` or `WindowManagerTrace.Entry`.
+
+```java
+    // Example adds an assertion to the check if layer is hidden by parent.
+    Result isHiddenByParent(String layerName) {
+        // Result should contain a details if assertion fails for any reason
+        // such as if layer is not found or layer is not hidden by parent
+        // or layer has no parent.
+        // ...
+    }
+```
+Then add a function to the trace subject `LayersTraceSubject` or `WmTraceSubject` which will add the assertion for testing. When the assertion is evaluated, the trace will first be filtered then the assertion will be applied to the remaining entries.
+
+```java
+    public LayersTraceSubject isHiddenByParent(String layerName) {
+        mChecker.add(entry -> entry.isHiddenByParent(layerName),
+                "isHiddenByParent(" + layerName + ")");
+        return this;
+    }
+```
+
+To use the new assertion:
+```java
+    // Check if "Chrome" layer is hidden by parent in the first trace entry.
+    assertThat(result).isHiddenByParent("Chrome").inTheBeginning();
+```
\ No newline at end of file
diff --git a/tests/FlickerTests/TEST_MAPPING b/tests/FlickerTests/TEST_MAPPING
new file mode 100644
index 0000000..55a6147
--- /dev/null
+++ b/tests/FlickerTests/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "postsubmit": [
+    {
+      "name": "FlickerTests"
+    }
+  ]
+}
\ No newline at end of file
diff --git a/tests/FlickerTests/lib/Android.mk b/tests/FlickerTests/lib/Android.mk
new file mode 100644
index 0000000..6a8dfe8
--- /dev/null
+++ b/tests/FlickerTests/lib/Android.mk
@@ -0,0 +1,48 @@
+#
+# Copyright (C) 2018 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.
+#
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := flickerlib
+LOCAL_MODULE_TAGS := tests optional
+# sign this with platform cert, so this test is allowed to call private platform apis
+LOCAL_CERTIFICATE := platform
+LOCAL_PRIVATE_PLATFORM_APIS := true
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_STATIC_JAVA_LIBRARIES := \
+   ub-janktesthelper \
+   cts-amwm-util \
+   platformprotosnano \
+   layersprotosnano \
+   truth-prebuilt \
+   sysui-helper \
+   launcher-helper-lib \
+
+include $(BUILD_STATIC_JAVA_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := flickerautomationhelperlib
+LOCAL_SDK_VERSION := current
+LOCAL_SRC_FILES := src/com/android/server/wm/flicker/AutomationUtils.java \
+    src/com/android/server/wm/flicker/WindowUtils.java
+LOCAL_STATIC_JAVA_LIBRARIES := sysui-helper \
+    launcher-helper-lib \
+    compatibility-device-util
+
+include $(BUILD_STATIC_JAVA_LIBRARY)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/Assertions.java b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/Assertions.java
new file mode 100644
index 0000000..84f9f871
--- /dev/null
+++ b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/Assertions.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2018 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.server.wm.flicker;
+
+import java.util.concurrent.TimeUnit;
+import java.util.function.Function;
+
+/**
+ * Collection of functional interfaces and classes representing assertions and their associated
+ * results. Assertions are functions that are applied over a single trace entry and returns a
+ * result which includes a detailed reason if the assertion fails.
+ */
+class Assertions {
+    /**
+     * Checks assertion on a single trace entry.
+     *
+     * @param <T> trace entry type to perform the assertion on.
+     */
+    @FunctionalInterface
+    interface TraceAssertion<T> extends Function<T, Result> {
+        /**
+         * Returns an assertion that represents the logical negation of this assertion.
+         *
+         * @return a assertion that represents the logical negation of this assertion
+         */
+        default TraceAssertion<T> negate() {
+            return (T t) -> apply(t).negate();
+        }
+    }
+
+    /**
+     * Checks assertion on a single layers trace entry.
+     */
+    @FunctionalInterface
+    interface LayersTraceAssertion extends TraceAssertion<LayersTrace.Entry> {
+
+    }
+
+    /**
+     * Utility class to store assertions with an identifier to help generate more useful debug
+     * data when dealing with multiple assertions.
+     */
+    static class NamedAssertion<T> {
+        final TraceAssertion<T> assertion;
+        final String name;
+
+        NamedAssertion(TraceAssertion<T> assertion, String name) {
+            this.assertion = assertion;
+            this.name = name;
+        }
+    }
+
+    /**
+     * Contains the result of an assertion including the reason for failed assertions.
+     */
+    static class Result {
+        static final String NEGATION_PREFIX = "!";
+        final boolean success;
+        final long timestamp;
+        final String assertionName;
+        final String reason;
+
+        Result(boolean success, long timestamp, String assertionName, String reason) {
+            this.success = success;
+            this.timestamp = timestamp;
+            this.assertionName = assertionName;
+            this.reason = reason;
+        }
+
+        Result(boolean success, String reason) {
+            this.success = success;
+            this.reason = reason;
+            this.assertionName = "";
+            this.timestamp = 0;
+        }
+
+        /**
+         * Returns the negated {@code Result} and adds a negation prefix to the assertion name.
+         */
+        Result negate() {
+            String negatedAssertionName;
+            if (this.assertionName.startsWith(NEGATION_PREFIX)) {
+                negatedAssertionName = this.assertionName.substring(NEGATION_PREFIX.length() + 1);
+            } else {
+                negatedAssertionName = NEGATION_PREFIX + this.assertionName;
+            }
+            return new Result(!this.success, this.timestamp, negatedAssertionName, this.reason);
+        }
+
+        boolean passed() {
+            return this.success;
+        }
+
+        boolean failed() {
+            return !this.success;
+        }
+
+        @Override
+        public String toString() {
+            return "Timestamp: " + prettyTimestamp(timestamp)
+                    + "\nAssertion: " + assertionName
+                    + "\nReason:   " + reason;
+        }
+
+        private String prettyTimestamp(long timestamp_ns) {
+            StringBuilder prettyTimestamp = new StringBuilder();
+            TimeUnit[] timeUnits = {TimeUnit.HOURS, TimeUnit.MINUTES, TimeUnit.SECONDS, TimeUnit
+                    .MILLISECONDS};
+            String[] unitSuffixes = {"h", "m", "s", "ms"};
+
+            for (int i = 0; i < timeUnits.length; i++) {
+                long convertedTime = timeUnits[i].convert(timestamp_ns, TimeUnit.NANOSECONDS);
+                timestamp_ns -= TimeUnit.NANOSECONDS.convert(convertedTime, timeUnits[i]);
+                prettyTimestamp.append(convertedTime).append(unitSuffixes[i]);
+            }
+
+            return prettyTimestamp.toString();
+        }
+    }
+}
diff --git a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/AssertionsChecker.java b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/AssertionsChecker.java
new file mode 100644
index 0000000..3c65d3c
--- /dev/null
+++ b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/AssertionsChecker.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2018 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.server.wm.flicker;
+
+import com.android.server.wm.flicker.Assertions.NamedAssertion;
+import com.android.server.wm.flicker.Assertions.Result;
+import com.android.server.wm.flicker.Assertions.TraceAssertion;
+
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * Captures some of the common logic in {@link LayersTraceSubject} and {@link WmTraceSubject}
+ * used to filter trace entries and combine multiple assertions.
+ *
+ * @param <T> trace entry type
+ */
+public class AssertionsChecker<T extends ITraceEntry> {
+    private boolean mFilterEntriesByRange = false;
+    private long mFilterStartTime = 0;
+    private long mFilterEndTime = 0;
+    private AssertionOption mOption = AssertionOption.NONE;
+    private List<NamedAssertion<T>> mAssertions = new LinkedList<>();
+
+    void add(Assertions.TraceAssertion<T> assertion, String name) {
+        mAssertions.add(new NamedAssertion<>(assertion, name));
+    }
+
+    void filterByRange(long startTime, long endTime) {
+        mFilterEntriesByRange = true;
+        mFilterStartTime = startTime;
+        mFilterEndTime = endTime;
+    }
+
+    private void setOption(AssertionOption option) {
+        if (mOption != AssertionOption.NONE && option != mOption) {
+            throw new IllegalArgumentException("Cannot use " + mOption + " option with "
+                    + option + " option.");
+        }
+        mOption = option;
+    }
+
+    public void checkFirstEntry() {
+        setOption(AssertionOption.CHECK_FIRST_ENTRY);
+    }
+
+    public void checkLastEntry() {
+        setOption(AssertionOption.CHECK_LAST_ENTRY);
+    }
+
+    public void checkChangingAssertions() {
+        setOption(AssertionOption.CHECK_CHANGING_ASSERTIONS);
+    }
+
+
+    /**
+     * Filters trace entries then runs assertions returning a list of failures.
+     *
+     * @param entries list of entries to perform assertions on
+     * @return list of failed assertion results
+     */
+    List<Result> test(List<T> entries) {
+        List<T> filteredEntries;
+        List<Result> failures;
+
+        if (mFilterEntriesByRange) {
+            filteredEntries = entries.stream()
+                    .filter(e -> ((e.getTimestamp() >= mFilterStartTime)
+                            && (e.getTimestamp() <= mFilterEndTime)))
+                    .collect(Collectors.toList());
+        } else {
+            filteredEntries = entries;
+        }
+
+        switch (mOption) {
+            case CHECK_CHANGING_ASSERTIONS:
+                return assertChanges(filteredEntries);
+            case CHECK_FIRST_ENTRY:
+                return assertEntry(filteredEntries.get(0));
+            case CHECK_LAST_ENTRY:
+                return assertEntry(filteredEntries.get(filteredEntries.size() - 1));
+        }
+        return assertAll(filteredEntries);
+    }
+
+    /**
+     * Steps through each trace entry checking if provided assertions are true in the order they
+     * are added. Each assertion must be true for at least a single trace entry.
+     *
+     * This can be used to check for asserting a change in property over a trace. Such as visibility
+     * for a window changes from true to false or top-most window changes from A to Bb and back to A
+     * again.
+     */
+    private List<Result> assertChanges(List<T> entries) {
+        List<Result> failures = new ArrayList<>();
+        int entryIndex = 0;
+        int assertionIndex = 0;
+        int lastPassedAssertionIndex = -1;
+
+        if (mAssertions.size() == 0) {
+            return failures;
+        }
+
+        while (assertionIndex < mAssertions.size() && entryIndex < entries.size()) {
+            TraceAssertion<T> currentAssertion = mAssertions.get(assertionIndex).assertion;
+            Result result = currentAssertion.apply(entries.get(entryIndex));
+            if (result.passed()) {
+                lastPassedAssertionIndex = assertionIndex;
+                entryIndex++;
+                continue;
+            }
+
+            if (lastPassedAssertionIndex != assertionIndex) {
+                failures.add(result);
+                break;
+            }
+            assertionIndex++;
+
+            if (assertionIndex == mAssertions.size()) {
+                failures.add(result);
+                break;
+            }
+        }
+
+        if (failures.isEmpty()) {
+            if (assertionIndex != mAssertions.size() - 1) {
+                String reason = "\nAssertion " + mAssertions.get(assertionIndex).name
+                        + " never became false";
+                reason += "\nPassed assertions: " + mAssertions.stream().limit(assertionIndex)
+                        .map(assertion -> assertion.name).collect(Collectors.joining(","));
+                reason += "\nUntested assertions: " + mAssertions.stream().skip(assertionIndex + 1)
+                        .map(assertion -> assertion.name).collect(Collectors.joining(","));
+
+                Result result = new Result(false /* success */, 0 /* timestamp */,
+                        "assertChanges", "Not all assertions passed." + reason);
+                failures.add(result);
+            }
+        }
+        return failures;
+    }
+
+    private List<Result> assertEntry(T entry) {
+        List<Result> failures = new ArrayList<>();
+        for (NamedAssertion<T> assertion : mAssertions) {
+            Result result = assertion.assertion.apply(entry);
+            if (result.failed()) {
+                failures.add(result);
+            }
+        }
+        return failures;
+    }
+
+    private List<Result> assertAll(List<T> entries) {
+        return mAssertions.stream().flatMap(
+                assertion -> entries.stream()
+                        .map(assertion.assertion)
+                        .filter(Result::failed))
+                .collect(Collectors.toList());
+    }
+
+    private enum AssertionOption {
+        NONE,
+        CHECK_CHANGING_ASSERTIONS,
+        CHECK_FIRST_ENTRY,
+        CHECK_LAST_ENTRY,
+    }
+}
diff --git a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/AutomationUtils.java b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/AutomationUtils.java
new file mode 100644
index 0000000..6bac675
--- /dev/null
+++ b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/AutomationUtils.java
@@ -0,0 +1,260 @@
+/*
+ * Copyright (C) 2018 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.server.wm.flicker;
+
+import static android.os.SystemClock.sleep;
+import static android.system.helpers.OverviewHelper.isRecentsInLauncher;
+import static android.view.Surface.ROTATION_0;
+
+import static com.android.compatibility.common.util.SystemUtil.runShellCommand;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.os.RemoteException;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.launcherhelper.LauncherStrategyFactory;
+import android.support.test.uiautomator.By;
+import android.support.test.uiautomator.BySelector;
+import android.support.test.uiautomator.Configurator;
+import android.support.test.uiautomator.UiDevice;
+import android.support.test.uiautomator.UiObject2;
+import android.support.test.uiautomator.Until;
+import android.util.Log;
+import android.util.Rational;
+import android.view.View;
+import android.view.ViewConfiguration;
+
+/**
+ * Collection of UI Automation helper functions.
+ */
+public class AutomationUtils {
+    private static final String SYSTEMUI_PACKAGE = "com.android.systemui";
+    private static final long FIND_TIMEOUT = 10000;
+    private static final long LONG_PRESS_TIMEOUT = ViewConfiguration.getLongPressTimeout() * 2L;
+    private static final String TAG = "FLICKER";
+
+    public static void wakeUpAndGoToHomeScreen() {
+        UiDevice device = UiDevice.getInstance(InstrumentationRegistry
+                .getInstrumentation());
+        try {
+            device.wakeUp();
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+        device.pressHome();
+    }
+
+    /**
+     * Sets {@link android.app.UiAutomation#waitForIdle(long, long)} global timeout to 0 causing
+     * the {@link android.app.UiAutomation#waitForIdle(long, long)} function to timeout instantly.
+     * This removes some delays when using the UIAutomator library required to create fast UI
+     * transitions.
+     */
+    static void setFastWait() {
+        Configurator.getInstance().setWaitForIdleTimeout(0);
+    }
+
+    /**
+     * Reverts {@link android.app.UiAutomation#waitForIdle(long, long)} to default behavior.
+     */
+    static void setDefaultWait() {
+        Configurator.getInstance().setWaitForIdleTimeout(10000);
+    }
+
+    public static boolean isQuickstepEnabled(UiDevice device) {
+        return device.findObject(By.res(SYSTEMUI_PACKAGE, "recent_apps")) == null;
+    }
+
+    public static void openQuickstep(UiDevice device) {
+        if (isQuickstepEnabled(device)) {
+            int height = device.getDisplayHeight();
+            UiObject2 navBar = device.findObject(By.res(SYSTEMUI_PACKAGE, "navigation_bar_frame"));
+
+            Rect navBarVisibleBounds;
+
+            // TODO(vishnun) investigate why this object cannot be found.
+            if (navBar != null) {
+                navBarVisibleBounds = navBar.getVisibleBounds();
+            } else {
+                Log.e(TAG, "Could not find nav bar, infer location");
+                navBarVisibleBounds = WindowUtils.getNavigationBarPosition(ROTATION_0);
+            }
+
+            // Swipe from nav bar to 2/3rd down the screen.
+            device.swipe(
+                    navBarVisibleBounds.centerX(), navBarVisibleBounds.centerY(),
+                    navBarVisibleBounds.centerX(), height * 2 / 3,
+                    (navBarVisibleBounds.centerY() - height * 2 / 3) / 100); // 100 px/step
+        } else {
+            try {
+                device.pressRecentApps();
+            } catch (RemoteException e) {
+                throw new RuntimeException(e);
+            }
+        }
+        BySelector RECENTS = By.res(SYSTEMUI_PACKAGE, "recents_view");
+
+        // use a long timeout to wait until recents populated
+        if (device.wait(
+                Until.findObject(isRecentsInLauncher()
+                        ? getLauncherOverviewSelector(device) : RECENTS),
+                10000) == null) {
+            fail("Recents didn't appear");
+        }
+        device.waitForIdle();
+    }
+
+    static void clearRecents(UiDevice device) {
+        if (isQuickstepEnabled(device)) {
+            openQuickstep(device);
+
+            for (int i = 0; i < 5; i++) {
+                device.swipe(device.getDisplayWidth() / 2,
+                        device.getDisplayHeight() / 2, device.getDisplayWidth(),
+                        device.getDisplayHeight() / 2,
+                        5);
+
+                BySelector clearAllSelector = By.res("com.google.android.apps.nexuslauncher",
+                        "clear_all_button");
+                UiObject2 clearAllButton = device.wait(Until.findObject(clearAllSelector), 100);
+                if (clearAllButton != null) {
+                    clearAllButton.click();
+                    return;
+                }
+            }
+        }
+    }
+
+    private static BySelector getLauncherOverviewSelector(UiDevice device) {
+        return By.res(device.getLauncherPackageName(), "overview_panel");
+    }
+
+    private static void longPressRecents(UiDevice device) {
+        BySelector recentsSelector = By.res(SYSTEMUI_PACKAGE, "recent_apps");
+        UiObject2 recentsButton = device.wait(Until.findObject(recentsSelector), FIND_TIMEOUT);
+        assertNotNull("Unable to find recents button", recentsButton);
+        recentsButton.click(LONG_PRESS_TIMEOUT);
+    }
+
+    public static void launchSplitScreen(UiDevice device) {
+        String mLauncherPackage = LauncherStrategyFactory.getInstance(device)
+                .getLauncherStrategy().getSupportedLauncherPackage();
+
+        if (isQuickstepEnabled(device)) {
+            // Quickstep enabled
+            openQuickstep(device);
+
+            BySelector overviewIconSelector = By.res(mLauncherPackage, "icon")
+                    .clazz(View.class);
+            UiObject2 overviewIcon = device.wait(Until.findObject(overviewIconSelector),
+                    FIND_TIMEOUT);
+            assertNotNull("Unable to find app icon in Overview", overviewIcon);
+            overviewIcon.click();
+
+            BySelector splitscreenButtonSelector = By.text("Split screen");
+            UiObject2 splitscreenButton = device.wait(Until.findObject(splitscreenButtonSelector),
+                    FIND_TIMEOUT);
+            assertNotNull("Unable to find Split screen button in Overview", overviewIcon);
+            splitscreenButton.click();
+        } else {
+            // Classic long press recents
+            longPressRecents(device);
+        }
+        // Wait for animation to complete.
+        sleep(2000);
+    }
+
+    public static void exitSplitScreen(UiDevice device) {
+        if (isQuickstepEnabled(device)) {
+            // Quickstep enabled
+            BySelector dividerSelector = By.res(SYSTEMUI_PACKAGE, "docked_divider_handle");
+            UiObject2 divider = device.wait(Until.findObject(dividerSelector), FIND_TIMEOUT);
+            assertNotNull("Unable to find Split screen divider", divider);
+
+            // Drag the split screen divider to the top of the screen
+            divider.drag(new Point(device.getDisplayWidth() / 2, 0), 400);
+        } else {
+            // Classic long press recents
+            longPressRecents(device);
+        }
+        // Wait for animation to complete.
+        sleep(2000);
+    }
+
+    static void resizeSplitScreen(UiDevice device, Rational windowHeightRatio) {
+        BySelector dividerSelector = By.res(SYSTEMUI_PACKAGE, "docked_divider_handle");
+        UiObject2 divider = device.wait(Until.findObject(dividerSelector), FIND_TIMEOUT);
+        assertNotNull("Unable to find Split screen divider", divider);
+        int destHeight =
+                (int) (WindowUtils.getDisplayBounds().height() * windowHeightRatio.floatValue());
+        // Drag the split screen divider to so that the ratio of top window height and bottom
+        // window height is windowHeightRatio
+        device.drag(divider.getVisibleBounds().centerX(), divider.getVisibleBounds().centerY(),
+                device.getDisplayWidth() / 2, destHeight, 10);
+        //divider.drag(new Point(device.getDisplayWidth() / 2, destHeight), 400)
+        divider = device.wait(Until.findObject(dividerSelector), FIND_TIMEOUT);
+
+        // Wait for animation to complete.
+        sleep(2000);
+    }
+
+    static void closePipWindow(UiDevice device) {
+        UiObject2 pipWindow = device.findObject(
+                By.res(SYSTEMUI_PACKAGE, "background"));
+        pipWindow.click();
+        UiObject2 exitPipObject = device.findObject(
+                By.res(SYSTEMUI_PACKAGE, "dismiss"));
+        exitPipObject.click();
+        // Wait for animation to complete.
+        sleep(2000);
+    }
+
+    static void expandPipWindow(UiDevice device) {
+        UiObject2 pipWindow = device.findObject(
+                By.res(SYSTEMUI_PACKAGE, "background"));
+        pipWindow.click();
+        pipWindow.click();
+    }
+
+    public static void stopPackage(Context context, String packageName) {
+        runShellCommand("am force-stop " + packageName);
+        int packageUid;
+        try {
+            packageUid = context.getPackageManager().getPackageUid(packageName, /* flags= */0);
+        } catch (PackageManager.NameNotFoundException e) {
+            return;
+        }
+        while (targetPackageIsRunning(packageUid)) {
+            try {
+                Thread.sleep(100);
+            } catch (InterruptedException e) {
+                //ignore
+            }
+        }
+    }
+
+    private static boolean targetPackageIsRunning(int uid) {
+        final String result = runShellCommand(
+                String.format("cmd activity get-uid-state %d", uid));
+        return !result.contains("(NONEXISTENT)");
+    }
+}
\ No newline at end of file
diff --git a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/ITraceEntry.java b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/ITraceEntry.java
new file mode 100644
index 0000000..9525f41
--- /dev/null
+++ b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/ITraceEntry.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2018 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.server.wm.flicker;
+
+/**
+ * Common interface for Layer and WindowManager trace entries.
+ */
+interface ITraceEntry {
+    /**
+     * @return timestamp of current entry
+     */
+    long getTimestamp();
+}
diff --git a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/LayersTrace.java b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/LayersTrace.java
new file mode 100644
index 0000000..660ec0f
--- /dev/null
+++ b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/LayersTrace.java
@@ -0,0 +1,412 @@
+/*
+ * Copyright (C) 2018 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.server.wm.flicker;
+
+import android.annotation.Nullable;
+import android.graphics.Rect;
+import android.surfaceflinger.nano.Layers.LayerProto;
+import android.surfaceflinger.nano.Layers.RectProto;
+import android.surfaceflinger.nano.Layers.RegionProto;
+import android.surfaceflinger.nano.Layerstrace.LayersTraceFileProto;
+import android.surfaceflinger.nano.Layerstrace.LayersTraceProto;
+import android.util.SparseArray;
+
+import com.android.server.wm.flicker.Assertions.Result;
+
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+/**
+ * Contains a collection of parsed Layers trace entries and assertions to apply over
+ * a single entry.
+ *
+ * Each entry is parsed into a list of {@link LayersTrace.Entry} objects.
+ */
+public class LayersTrace {
+    final private List<Entry> mEntries;
+    @Nullable
+    final private Path mSource;
+
+    private LayersTrace(List<Entry> entries, Path source) {
+        this.mEntries = entries;
+        this.mSource = source;
+    }
+
+    /**
+     * Parses {@code LayersTraceFileProto} from {@code data} and uses the proto to generates a list
+     * of trace entries, storing the flattened layers into its hierarchical structure.
+     *
+     * @param data   binary proto data
+     * @param source Path to source of data for additional debug information
+     */
+    static LayersTrace parseFrom(byte[] data, Path source) {
+        List<Entry> entries = new ArrayList<>();
+        LayersTraceFileProto fileProto;
+        try {
+            fileProto = LayersTraceFileProto.parseFrom(data);
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+        for (LayersTraceProto traceProto : fileProto.entry) {
+            Entry entry = Entry.fromFlattenedLayers(traceProto.elapsedRealtimeNanos,
+                    traceProto.layers.layers);
+            entries.add(entry);
+        }
+        return new LayersTrace(entries, source);
+    }
+
+    /**
+     * Parses {@code LayersTraceFileProto} from {@code data} and uses the proto to generates a list
+     * of trace entries, storing the flattened layers into its hierarchical structure.
+     *
+     * @param data binary proto data
+     */
+    static LayersTrace parseFrom(byte[] data) {
+        return parseFrom(data, null);
+    }
+
+    List<Entry> getEntries() {
+        return mEntries;
+    }
+
+    Entry getEntry(long timestamp) {
+        Optional<Entry> entry = mEntries.stream()
+                .filter(e -> e.getTimestamp() == timestamp)
+                .findFirst();
+        if (!entry.isPresent()) {
+            throw new RuntimeException("Entry does not exist for timestamp " + timestamp);
+        }
+        return entry.get();
+    }
+
+    Optional<Path> getSource() {
+        return Optional.ofNullable(mSource);
+    }
+
+    /**
+     * Represents a single Layer trace entry.
+     */
+    static class Entry implements ITraceEntry {
+        private long mTimestamp;
+        private List<Layer> mRootLayers; // hierarchical representation of layers
+        private List<Layer> mFlattenedLayers = null;
+
+        private Entry(long timestamp, List<Layer> rootLayers) {
+            this.mTimestamp = timestamp;
+            this.mRootLayers = rootLayers;
+        }
+
+        /**
+         * Constructs the layer hierarchy from a flattened list of layers.
+         */
+        static Entry fromFlattenedLayers(long timestamp, LayerProto[] protos) {
+            SparseArray<Layer> layerMap = new SparseArray<>();
+            ArrayList<Layer> orphans = new ArrayList<>();
+            for (LayerProto proto : protos) {
+                int id = proto.id;
+                int parentId = proto.parent;
+
+                Layer newLayer = layerMap.get(id);
+                if (newLayer == null) {
+                    newLayer = new Layer(proto);
+                    layerMap.append(id, newLayer);
+                } else if (newLayer.mProto != null) {
+                    throw new RuntimeException("Duplicate layer id found:" + id);
+                } else {
+                    newLayer.mProto = proto;
+                    orphans.remove(newLayer);
+                }
+
+                // add parent placeholder
+                if (layerMap.get(parentId) == null) {
+                    Layer orphanLayer = new Layer(null);
+                    layerMap.append(parentId, orphanLayer);
+                    orphans.add(orphanLayer);
+                }
+                layerMap.get(parentId).addChild(newLayer);
+                newLayer.addParent(layerMap.get(parentId));
+            }
+
+            // Fail if we find orphan layers.
+            orphans.remove(layerMap.get(-1));
+            orphans.forEach(orphan -> {
+                String childNodes = orphan.mChildren.stream().map(node ->
+                        Integer.toString(node.getId())).collect(Collectors.joining(", "));
+                int orphanId = orphan.mChildren.get(0).mProto.parent;
+                throw new RuntimeException(
+                        "Failed to parse layers trace. Found orphan layers with parent "
+                                + "layer id:" + orphanId + " : " + childNodes);
+            });
+
+            return new Entry(timestamp, layerMap.get(-1).mChildren);
+        }
+
+        /**
+         * Extracts {@link Rect} from {@link RectProto}.
+         */
+        private static Rect extract(RectProto proto) {
+            return new Rect(proto.left, proto.top, proto.right, proto.bottom);
+        }
+
+        /**
+         * Extracts {@link Rect} from {@link RegionProto} by returning a rect that encompasses all
+         * the rects making up the region.
+         */
+        private static Rect extract(RegionProto regionProto) {
+            Rect region = new Rect();
+            for (RectProto proto : regionProto.rect) {
+                region.union(proto.left, proto.top, proto.right, proto.bottom);
+            }
+            return region;
+        }
+
+        /**
+         * Checks if a region specified by {@code testRect} is covered by all visible layers.
+         */
+        Result coversRegion(Rect testRect) {
+            String assertionName = "coversRegion";
+            Collection<Layer> layers = asFlattenedLayers();
+
+            for (int x = testRect.left; x < testRect.right; x++) {
+                for (int y = testRect.top; y < testRect.bottom; y++) {
+                    boolean emptyRegionFound = true;
+                    for (Layer layer : layers) {
+                        if (layer.isInvisible() || layer.isHiddenByParent()) {
+                            continue;
+                        }
+                        for (RectProto rectProto : layer.mProto.visibleRegion.rect) {
+                            Rect r = extract(rectProto);
+                            if (r.contains(x, y)) {
+                                y = r.bottom;
+                                emptyRegionFound = false;
+                            }
+                        }
+                    }
+                    if (emptyRegionFound) {
+                        String reason = "Region to test: " + testRect
+                                + "\nfirst empty point: " + x + ", " + y;
+                        reason += "\nvisible regions:";
+                        for (Layer layer : layers) {
+                            if (layer.isInvisible() || layer.isHiddenByParent()) {
+                                continue;
+                            }
+                            Rect r = extract(layer.mProto.visibleRegion);
+                            reason += "\n" + layer.mProto.name + r.toString();
+                        }
+                        return new Result(false /* success */, this.mTimestamp, assertionName,
+                                reason);
+                    }
+                }
+            }
+            String info = "Region covered: " + testRect;
+            return new Result(true /* success */, this.mTimestamp, assertionName, info);
+        }
+
+        /**
+         * Checks if a layer with name {@code layerName} has a visible region
+         * {@code expectedVisibleRegion}.
+         */
+        Result hasVisibleRegion(String layerName, Rect expectedVisibleRegion) {
+            String assertionName = "hasVisibleRegion";
+            String reason = "Could not find " + layerName;
+            for (Layer layer : asFlattenedLayers()) {
+                if (layer.mProto.name.contains(layerName)) {
+                    if (layer.isHiddenByParent()) {
+                        reason = layer.getHiddenByParentReason();
+                        continue;
+                    }
+                    if (layer.isInvisible()) {
+                        reason = layer.getVisibilityReason();
+                        continue;
+                    }
+                    Rect visibleRegion = extract(layer.mProto.visibleRegion);
+                    if (visibleRegion.equals(expectedVisibleRegion)) {
+                        return new Result(true /* success */, this.mTimestamp, assertionName,
+                                layer.mProto.name + "has visible region " + expectedVisibleRegion);
+                    }
+                    reason = layer.mProto.name + " has visible region:" + visibleRegion + " "
+                            + "expected:" + expectedVisibleRegion;
+                }
+            }
+            return new Result(false /* success */, this.mTimestamp, assertionName, reason);
+        }
+
+        /**
+         * Checks if a layer with name {@code layerName} is visible.
+         */
+        Result isVisible(String layerName) {
+            String assertionName = "isVisible";
+            String reason = "Could not find " + layerName;
+            for (Layer layer : asFlattenedLayers()) {
+                if (layer.mProto.name.contains(layerName)) {
+                    if (layer.isHiddenByParent()) {
+                        reason = layer.getHiddenByParentReason();
+                        continue;
+                    }
+                    if (layer.isInvisible()) {
+                        reason = layer.getVisibilityReason();
+                        continue;
+                    }
+                    return new Result(true /* success */, this.mTimestamp, assertionName,
+                            layer.mProto.name + " is visible");
+                }
+            }
+            return new Result(false /* success */, this.mTimestamp, assertionName, reason);
+        }
+
+        @Override
+        public long getTimestamp() {
+            return mTimestamp;
+        }
+
+        List<Layer> getRootLayers() {
+            return mRootLayers;
+        }
+
+        List<Layer> asFlattenedLayers() {
+            if (mFlattenedLayers == null) {
+                mFlattenedLayers = new ArrayList<>();
+                ArrayList<Layer> pendingLayers = new ArrayList<>(this.mRootLayers);
+                while (!pendingLayers.isEmpty()) {
+                    Layer layer = pendingLayers.remove(0);
+                    mFlattenedLayers.add(layer);
+                    pendingLayers.addAll(layer.mChildren);
+                }
+            }
+            return mFlattenedLayers;
+        }
+
+        Rect getVisibleBounds(String layerName) {
+            List<Layer> layers = asFlattenedLayers();
+            for (Layer layer : layers) {
+                if (layer.mProto.name.contains(layerName) && layer.isVisible()) {
+                    return extract(layer.mProto.visibleRegion);
+                }
+            }
+            return new Rect(0, 0, 0, 0);
+        }
+    }
+
+    /**
+     * Represents a single layer with links to its parent and child layers.
+     */
+    static class Layer {
+        @Nullable
+        LayerProto mProto;
+        List<Layer> mChildren;
+        @Nullable
+        Layer mParent = null;
+
+        private Layer(LayerProto proto) {
+            this.mProto = proto;
+            this.mChildren = new ArrayList<>();
+        }
+
+        private void addChild(Layer childLayer) {
+            this.mChildren.add(childLayer);
+        }
+
+        private void addParent(Layer parentLayer) {
+            this.mParent = parentLayer;
+        }
+
+        int getId() {
+            return mProto.id;
+        }
+
+        boolean isActiveBufferEmpty() {
+            return this.mProto.activeBuffer == null || this.mProto.activeBuffer.height == 0
+                    || this.mProto.activeBuffer.width == 0;
+        }
+
+        boolean isVisibleRegionEmpty() {
+            if (this.mProto.visibleRegion == null) {
+                return true;
+            }
+            Rect visibleRect = Entry.extract(this.mProto.visibleRegion);
+            return visibleRect.height() == 0 || visibleRect.width() == 0;
+        }
+
+        boolean isHidden() {
+            return (this.mProto.flags & /* FLAG_HIDDEN */ 0x1) != 0x0;
+        }
+
+        boolean isVisible() {
+            return (!isActiveBufferEmpty() || isColorLayer()) &&
+                    !isHidden() && this.mProto.color.a > 0 && !isVisibleRegionEmpty();
+        }
+
+        boolean isColorLayer() {
+            return this.mProto.type.equals("ColorLayer");
+        }
+
+        boolean isRootLayer() {
+            return mParent == null || mParent.mProto == null;
+        }
+
+        boolean isInvisible() {
+            return !isVisible();
+        }
+
+        boolean isHiddenByParent() {
+            return !isRootLayer() && (mParent.isHidden() || mParent.isHiddenByParent());
+        }
+
+        String getHiddenByParentReason() {
+            String reason = "Layer " + mProto.name;
+            if (isHiddenByParent()) {
+                reason += " is hidden by parent: " + mParent.mProto.name;
+            } else {
+                reason += " is not hidden by parent: " + mParent.mProto.name;
+            }
+            return reason;
+        }
+
+        String getVisibilityReason() {
+            String reason = "Layer " + mProto.name;
+            if (isVisible()) {
+                reason += " is visible:";
+            } else {
+                reason += " is invisible:";
+                if (this.mProto.activeBuffer == null) {
+                    reason += " activeBuffer=null";
+                } else if (this.mProto.activeBuffer.height == 0) {
+                    reason += " activeBuffer.height=0";
+                } else if (this.mProto.activeBuffer.width == 0) {
+                    reason += " activeBuffer.width=0";
+                }
+                if (!isColorLayer()) {
+                    reason += " type != ColorLayer";
+                }
+                if (isHidden()) {
+                    reason += " flags=" + this.mProto.flags + " (FLAG_HIDDEN set)";
+                }
+                if (this.mProto.color.a == 0) {
+                    reason += " color.a=0";
+                }
+                if (isVisibleRegionEmpty()) {
+                    reason += " visible region is empty";
+                }
+            }
+            return reason;
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/LayersTraceSubject.java b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/LayersTraceSubject.java
new file mode 100644
index 0000000..b4c97e4
--- /dev/null
+++ b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/LayersTraceSubject.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2018 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.server.wm.flicker;
+
+import static com.google.common.truth.Truth.assertAbout;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import android.annotation.Nullable;
+import android.graphics.Rect;
+
+import com.android.server.wm.flicker.Assertions.Result;
+import com.android.server.wm.flicker.LayersTrace.Entry;
+import com.android.server.wm.flicker.TransitionRunner.TransitionResult;
+
+import com.google.common.truth.FailureStrategy;
+import com.google.common.truth.Subject;
+import com.google.common.truth.SubjectFactory;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * Truth subject for {@link LayersTrace} objects.
+ */
+public class LayersTraceSubject extends Subject<LayersTraceSubject, LayersTrace> {
+    // Boiler-plate Subject.Factory for LayersTraceSubject
+    private static final SubjectFactory<LayersTraceSubject, LayersTrace> FACTORY =
+            new SubjectFactory<LayersTraceSubject, LayersTrace>() {
+                @Override
+                public LayersTraceSubject getSubject(
+                        FailureStrategy fs, @Nullable LayersTrace target) {
+                    return new LayersTraceSubject(fs, target);
+                }
+            };
+
+    private AssertionsChecker<Entry> mChecker = new AssertionsChecker<>();
+
+    private LayersTraceSubject(FailureStrategy fs, @Nullable LayersTrace subject) {
+        super(fs, subject);
+    }
+
+    // User-defined entry point
+    public static LayersTraceSubject assertThat(@Nullable LayersTrace entry) {
+        return assertAbout(FACTORY).that(entry);
+    }
+
+    // User-defined entry point
+    public static LayersTraceSubject assertThat(@Nullable TransitionResult result) {
+        LayersTrace entries = LayersTrace.parseFrom(result.getLayersTrace(),
+                result.getLayersTracePath());
+        return assertWithMessage(result.toString()).about(FACTORY).that(entries);
+    }
+
+    // Static method for getting the subject factory (for use with assertAbout())
+    public static SubjectFactory<LayersTraceSubject, LayersTrace> entries() {
+        return FACTORY;
+    }
+
+    public void forAllEntries() {
+        test();
+    }
+
+    public void forRange(long startTime, long endTime) {
+        mChecker.filterByRange(startTime, endTime);
+        test();
+    }
+
+    public LayersTraceSubject then() {
+        mChecker.checkChangingAssertions();
+        return this;
+    }
+
+    public void inTheBeginning() {
+        if (getSubject().getEntries().isEmpty()) {
+            fail("No entries found.");
+        }
+        mChecker.checkFirstEntry();
+        test();
+    }
+
+    public void atTheEnd() {
+        if (getSubject().getEntries().isEmpty()) {
+            fail("No entries found.");
+        }
+        mChecker.checkLastEntry();
+        test();
+    }
+
+    private void test() {
+        List<Result> failures = mChecker.test(getSubject().getEntries());
+        if (!failures.isEmpty()) {
+            String failureLogs = failures.stream().map(Result::toString)
+                    .collect(Collectors.joining("\n"));
+            String tracePath = "";
+            if (getSubject().getSource().isPresent()) {
+                tracePath = "\nLayers Trace can be found in: "
+                        + getSubject().getSource().get().toAbsolutePath() + "\n";
+            }
+            fail(tracePath + failureLogs);
+        }
+    }
+
+    public LayersTraceSubject coversRegion(Rect rect) {
+        mChecker.add(entry -> entry.coversRegion(rect),
+                "coversRegion(" + rect + ")");
+        return this;
+    }
+
+    public LayersTraceSubject hasVisibleRegion(String layerName, Rect size) {
+        mChecker.add(entry -> entry.hasVisibleRegion(layerName, size),
+                "hasVisibleRegion(" + layerName + size + ")");
+        return this;
+    }
+
+    public LayersTraceSubject showsLayer(String layerName) {
+        mChecker.add(entry -> entry.isVisible(layerName),
+                "showsLayer(" + layerName + ")");
+        return this;
+    }
+
+    public LayersTraceSubject hidesLayer(String layerName) {
+        mChecker.add(entry -> entry.isVisible(layerName).negate(),
+                "hidesLayer(" + layerName + ")");
+        return this;
+    }
+}
diff --git a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/TransitionRunner.java b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/TransitionRunner.java
new file mode 100644
index 0000000..f6e8192
--- /dev/null
+++ b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/TransitionRunner.java
@@ -0,0 +1,423 @@
+/*
+ * Copyright (C) 2018 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.server.wm.flicker;
+
+import android.annotation.Nullable;
+import android.support.annotation.VisibleForTesting;
+import android.support.test.InstrumentationRegistry;
+import android.util.Log;
+
+import com.android.server.wm.flicker.monitor.ITransitionMonitor;
+import com.android.server.wm.flicker.monitor.LayersTraceMonitor;
+import com.android.server.wm.flicker.monitor.ScreenRecorder;
+import com.android.server.wm.flicker.monitor.WindowAnimationFrameStatsMonitor;
+import com.android.server.wm.flicker.monitor.WindowManagerTraceMonitor;
+
+import com.google.common.io.Files;
+
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * Builds and runs UI transitions capturing test artifacts.
+ *
+ * User can compose a transition from simpler steps, specifying setup and teardown steps. During
+ * a transition, Layers trace, WindowManager trace, screen recordings and window animation frame
+ * stats can be captured.
+ *
+ * <pre>
+ * Transition builder options:
+ *  {@link TransitionBuilder#run(Runnable)} run transition under test. Monitors will be started
+ *  before the transition and stopped after the transition is completed.
+ *  {@link TransitionBuilder#repeat(int)} repeat transitions under test multiple times recording
+ *  result for each run.
+ *  {@link TransitionBuilder#withTag(String)} specify a string identifier used to prefix logs and
+ *  artifacts generated.
+ *  {@link TransitionBuilder#runBeforeAll(Runnable)} run setup transitions once before all other
+ *  transition are run to set up an initial state on device.
+ *  {@link TransitionBuilder#runBefore(Runnable)} run setup transitions before each test transition
+ *  run.
+ *  {@link TransitionBuilder#runAfter(Runnable)} run teardown transitions after each test
+ *  transition.
+ *  {@link TransitionBuilder#runAfter(Runnable)} run teardown transitions once after all
+ *  other transition  are run.
+ *  {@link TransitionBuilder#includeJankyRuns()} disables {@link WindowAnimationFrameStatsMonitor}
+ *  to monitor janky frames. If janky frames are detected, then the test run is skipped. This
+ *  monitor is enabled by default.
+ *  {@link TransitionBuilder#skipLayersTrace()} disables {@link LayersTraceMonitor} used to
+ *  capture Layers trace during a transition. This monitor is enabled by default.
+ *  {@link TransitionBuilder#skipWindowManagerTrace()} disables {@link WindowManagerTraceMonitor}
+ *  used to capture WindowManager trace during a transition. This monitor is enabled by
+ *  default.
+ *  {@link TransitionBuilder#recordAllRuns()} records the screen contents and saves it to a file.
+ *  All the runs including setup and teardown transitions are included in the recording. This
+ *  monitor is used for debugging purposes.
+ *  {@link TransitionBuilder#recordEachRun()} records the screen contents during test transitions
+ *  and saves it to a file for each run. This monitor is used for debugging purposes.
+ *
+ * Example transition to capture WindowManager and Layers trace when opening a test app:
+ * {@code
+ * TransitionRunner.newBuilder()
+ *      .withTag("OpenTestAppFast")
+ *      .runBeforeAll(UiAutomationLib::wakeUp)
+ *      .runBeforeAll(UiAutomationLib::UnlockDevice)
+ *      .runBeforeAll(UiAutomationLib::openTestApp)
+ *      .runBefore(UiAutomationLib::closeTestApp)
+ *      .run(UiAutomationLib::openTestApp)
+ *      .runAfterAll(UiAutomationLib::closeTestApp)
+ *      .repeat(5)
+ *      .build()
+ *      .run();
+ * }
+ * </pre>
+ */
+class TransitionRunner {
+    private static final String TAG = "FLICKER";
+    private final ScreenRecorder mScreenRecorder;
+    private final WindowManagerTraceMonitor mWmTraceMonitor;
+    private final LayersTraceMonitor mLayersTraceMonitor;
+    private final WindowAnimationFrameStatsMonitor mFrameStatsMonitor;
+
+    private final List<ITransitionMonitor> mAllRunsMonitors;
+    private final List<ITransitionMonitor> mPerRunMonitors;
+    private final List<Runnable> mBeforeAlls;
+    private final List<Runnable> mBefores;
+    private final List<Runnable> mTransitions;
+    private final List<Runnable> mAfters;
+    private final List<Runnable> mAfterAlls;
+
+    private final int mIterations;
+    private final String mTestTag;
+
+    @Nullable
+    private List<TransitionResult> mResults = null;
+
+    private TransitionRunner(TransitionBuilder builder) {
+        mScreenRecorder = builder.mScreenRecorder;
+        mWmTraceMonitor = builder.mWmTraceMonitor;
+        mLayersTraceMonitor = builder.mLayersTraceMonitor;
+        mFrameStatsMonitor = builder.mFrameStatsMonitor;
+
+        mAllRunsMonitors = builder.mAllRunsMonitors;
+        mPerRunMonitors = builder.mPerRunMonitors;
+        mBeforeAlls = builder.mBeforeAlls;
+        mBefores = builder.mBefores;
+        mTransitions = builder.mTransitions;
+        mAfters = builder.mAfters;
+        mAfterAlls = builder.mAfterAlls;
+
+        mIterations = builder.mIterations;
+        mTestTag = builder.mTestTag;
+    }
+
+    static TransitionBuilder newBuilder() {
+        return new TransitionBuilder();
+    }
+
+    /**
+     * Runs the composed transition and calls monitors at the appropriate stages. If jank monitor
+     * is enabled, transitions with jank are skipped.
+     *
+     * @return itself
+     */
+    TransitionRunner run() {
+        mResults = new ArrayList<>();
+        mAllRunsMonitors.forEach(ITransitionMonitor::start);
+        mBeforeAlls.forEach(Runnable::run);
+        for (int iteration = 0; iteration < mIterations; iteration++) {
+            mBefores.forEach(Runnable::run);
+            mPerRunMonitors.forEach(ITransitionMonitor::start);
+            mTransitions.forEach(Runnable::run);
+            mPerRunMonitors.forEach(ITransitionMonitor::stop);
+            mAfters.forEach(Runnable::run);
+            if (runJankFree() && mFrameStatsMonitor.jankyFramesDetected()) {
+                String msg = String.format("Skipping iteration %d/%d for test %s due to jank. %s",
+                        iteration, mIterations - 1, mTestTag, mFrameStatsMonitor.toString());
+                Log.e(TAG, msg);
+                continue;
+            }
+            mResults.add(saveResult(iteration));
+        }
+        mAfterAlls.forEach(Runnable::run);
+        mAllRunsMonitors.forEach(monitor -> {
+            monitor.stop();
+            Path path = monitor.save(mTestTag);
+            Log.e(TAG, "Video saved to " + path.toString());
+        });
+        return this;
+    }
+
+    /**
+     * Returns a list of transition results.
+     *
+     * @return list of transition results.
+     */
+    List<TransitionResult> getResults() {
+        if (mResults == null) {
+            throw new IllegalStateException("Results do not exist!");
+        }
+        return mResults;
+    }
+
+    /**
+     * Deletes all transition results that are not marked for saving.
+     *
+     * @return list of transition results.
+     */
+    void deleteResults() {
+        if (mResults == null) {
+            return;
+        }
+        mResults.stream()
+                .filter(TransitionResult::canDelete)
+                .forEach(TransitionResult::delete);
+        mResults = null;
+    }
+
+    /**
+     * Saves monitor results to file.
+     *
+     * @return object containing paths to test artifacts
+     */
+    private TransitionResult saveResult(int iteration) {
+        Path windowTrace = null;
+        Path layerTrace = null;
+        Path screenCaptureVideo = null;
+
+        if (mPerRunMonitors.contains(mWmTraceMonitor)) {
+            windowTrace = mWmTraceMonitor.save(mTestTag, iteration);
+        }
+        if (mPerRunMonitors.contains(mLayersTraceMonitor)) {
+            layerTrace = mLayersTraceMonitor.save(mTestTag, iteration);
+        }
+        if (mPerRunMonitors.contains(mScreenRecorder)) {
+            screenCaptureVideo = mScreenRecorder.save(mTestTag, iteration);
+        }
+        return new TransitionResult(layerTrace, windowTrace, screenCaptureVideo);
+    }
+
+    private boolean runJankFree() {
+        return mPerRunMonitors.contains(mFrameStatsMonitor);
+    }
+
+    public String getTestTag() {
+        return mTestTag;
+    }
+
+    /**
+     * Stores paths to all test artifacts.
+     */
+    @VisibleForTesting
+    public static class TransitionResult {
+        @Nullable
+        final Path layersTrace;
+        @Nullable
+        final Path windowManagerTrace;
+        @Nullable
+        final Path screenCaptureVideo;
+        private boolean flaggedForSaving;
+
+        TransitionResult(@Nullable Path layersTrace, @Nullable Path windowManagerTrace,
+                @Nullable Path screenCaptureVideo) {
+            this.layersTrace = layersTrace;
+            this.windowManagerTrace = windowManagerTrace;
+            this.screenCaptureVideo = screenCaptureVideo;
+        }
+
+        void flagForSaving() {
+            flaggedForSaving = true;
+        }
+
+        boolean canDelete() {
+            return !flaggedForSaving;
+        }
+
+        boolean layersTraceExists() {
+            return layersTrace != null && layersTrace.toFile().exists();
+        }
+
+        byte[] getLayersTrace() {
+            try {
+                return Files.toByteArray(this.layersTrace.toFile());
+            } catch (IOException e) {
+                throw new RuntimeException(e);
+            }
+        }
+
+        Path getLayersTracePath() {
+            return layersTrace;
+        }
+
+        boolean windowManagerTraceExists() {
+            return windowManagerTrace != null && windowManagerTrace.toFile().exists();
+        }
+
+        public byte[] getWindowManagerTrace() {
+            try {
+                return Files.toByteArray(this.windowManagerTrace.toFile());
+            } catch (IOException e) {
+                throw new RuntimeException(e);
+            }
+        }
+
+        Path getWindowManagerTracePath() {
+            return windowManagerTrace;
+        }
+
+        boolean screenCaptureVideoExists() {
+            return screenCaptureVideo != null && screenCaptureVideo.toFile().exists();
+        }
+
+        Path screenCaptureVideoPath() {
+            return screenCaptureVideo;
+        }
+
+        void delete() {
+            if (layersTraceExists()) layersTrace.toFile().delete();
+            if (windowManagerTraceExists()) windowManagerTrace.toFile().delete();
+            if (screenCaptureVideoExists()) screenCaptureVideo.toFile().delete();
+        }
+    }
+
+    /**
+     * Builds a {@link TransitionRunner} instance.
+     */
+    static class TransitionBuilder {
+        private ScreenRecorder mScreenRecorder;
+        private WindowManagerTraceMonitor mWmTraceMonitor;
+        private LayersTraceMonitor mLayersTraceMonitor;
+        private WindowAnimationFrameStatsMonitor mFrameStatsMonitor;
+
+        private List<ITransitionMonitor> mAllRunsMonitors = new LinkedList<>();
+        private List<ITransitionMonitor> mPerRunMonitors = new LinkedList<>();
+        private List<Runnable> mBeforeAlls = new LinkedList<>();
+        private List<Runnable> mBefores = new LinkedList<>();
+        private List<Runnable> mTransitions = new LinkedList<>();
+        private List<Runnable> mAfters = new LinkedList<>();
+        private List<Runnable> mAfterAlls = new LinkedList<>();
+
+        private boolean mRunJankFree = true;
+        private boolean mCaptureWindowManagerTrace = true;
+        private boolean mCaptureLayersTrace = true;
+        private boolean mRecordEachRun = false;
+        private int mIterations = 1;
+        private String mTestTag = "";
+
+        private boolean mRecordAllRuns = false;
+
+        TransitionBuilder() {
+            mScreenRecorder = new ScreenRecorder();
+            mWmTraceMonitor = new WindowManagerTraceMonitor();
+            mLayersTraceMonitor = new LayersTraceMonitor();
+            mFrameStatsMonitor = new
+                    WindowAnimationFrameStatsMonitor(InstrumentationRegistry.getInstrumentation());
+        }
+
+        TransitionRunner build() {
+            if (mCaptureWindowManagerTrace) {
+                mPerRunMonitors.add(mWmTraceMonitor);
+            }
+
+            if (mCaptureLayersTrace) {
+                mPerRunMonitors.add(mLayersTraceMonitor);
+            }
+
+            if (mRunJankFree) {
+                mPerRunMonitors.add(mFrameStatsMonitor);
+            }
+
+            if (mRecordAllRuns) {
+                mAllRunsMonitors.add(mScreenRecorder);
+            }
+
+            if (mRecordEachRun) {
+                mPerRunMonitors.add(mScreenRecorder);
+            }
+
+            return new TransitionRunner(this);
+        }
+
+        TransitionBuilder runBeforeAll(Runnable runnable) {
+            mBeforeAlls.add(runnable);
+            return this;
+        }
+
+        TransitionBuilder runBefore(Runnable runnable) {
+            mBefores.add(runnable);
+            return this;
+        }
+
+        TransitionBuilder run(Runnable runnable) {
+            mTransitions.add(runnable);
+            return this;
+        }
+
+        TransitionBuilder runAfter(Runnable runnable) {
+            mAfters.add(runnable);
+            return this;
+        }
+
+        TransitionBuilder runAfterAll(Runnable runnable) {
+            mAfterAlls.add(runnable);
+            return this;
+        }
+
+        TransitionBuilder repeat(int iterations) {
+            mIterations = iterations;
+            return this;
+        }
+
+        TransitionBuilder skipWindowManagerTrace() {
+            mCaptureWindowManagerTrace = false;
+            return this;
+        }
+
+        TransitionBuilder skipLayersTrace() {
+            mCaptureLayersTrace = false;
+            return this;
+        }
+
+        TransitionBuilder includeJankyRuns() {
+            mRunJankFree = false;
+            return this;
+        }
+
+        TransitionBuilder recordEachRun() {
+            if (mRecordAllRuns) {
+                throw new IllegalArgumentException("Invalid option with recordAllRuns");
+            }
+            mRecordEachRun = true;
+            return this;
+        }
+
+        TransitionBuilder recordAllRuns() {
+            if (mRecordEachRun) {
+                throw new IllegalArgumentException("Invalid option with recordEachRun");
+            }
+            mRecordAllRuns = true;
+            return this;
+        }
+
+        TransitionBuilder withTag(String testTag) {
+            mTestTag = testTag;
+            return this;
+        }
+    }
+}
diff --git a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/WindowManagerTrace.java b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/WindowManagerTrace.java
new file mode 100644
index 0000000..e3592eb
--- /dev/null
+++ b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/WindowManagerTrace.java
@@ -0,0 +1,240 @@
+/*
+ * Copyright (C) 2018 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.server.wm.flicker;
+
+import android.annotation.Nullable;
+
+import com.android.server.wm.flicker.Assertions.Result;
+import com.android.server.wm.nano.AppWindowTokenProto;
+import com.android.server.wm.nano.StackProto;
+import com.android.server.wm.nano.TaskProto;
+import com.android.server.wm.nano.WindowManagerTraceFileProto;
+import com.android.server.wm.nano.WindowManagerTraceProto;
+import com.android.server.wm.nano.WindowStateProto;
+import com.android.server.wm.nano.WindowTokenProto;
+
+import com.google.protobuf.nano.InvalidProtocolBufferNanoException;
+
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+
+/**
+ * Contains a collection of parsed WindowManager trace entries and assertions to apply over
+ * a single entry.
+ *
+ * Each entry is parsed into a list of {@link WindowManagerTrace.Entry} objects.
+ */
+public class WindowManagerTrace {
+    private static final int DEFAULT_DISPLAY = 0;
+    private final List<Entry> mEntries;
+    @Nullable
+    final private Path mSource;
+
+    private WindowManagerTrace(List<Entry> entries, Path source) {
+        this.mEntries = entries;
+        this.mSource = source;
+    }
+
+    /**
+     * Parses {@code WindowManagerTraceFileProto} from {@code data} and uses the proto to
+     * generates a list of trace entries.
+     *
+     * @param data   binary proto data
+     * @param source Path to source of data for additional debug information
+     */
+    static WindowManagerTrace parseFrom(byte[] data, Path source) {
+        List<Entry> entries = new ArrayList<>();
+
+        WindowManagerTraceFileProto fileProto;
+        try {
+            fileProto = WindowManagerTraceFileProto.parseFrom(data);
+        } catch (InvalidProtocolBufferNanoException e) {
+            throw new RuntimeException(e);
+        }
+        for (WindowManagerTraceProto entryProto : fileProto.entry) {
+            entries.add(new Entry(entryProto));
+        }
+        return new WindowManagerTrace(entries, source);
+    }
+
+    static WindowManagerTrace parseFrom(byte[] data) {
+        return parseFrom(data, null);
+    }
+
+    public List<Entry> getEntries() {
+        return mEntries;
+    }
+
+    Entry getEntry(long timestamp) {
+        Optional<Entry> entry = mEntries.stream()
+                .filter(e -> e.getTimestamp() == timestamp)
+                .findFirst();
+        if (!entry.isPresent()) {
+            throw new RuntimeException("Entry does not exist for timestamp " + timestamp);
+        }
+        return entry.get();
+    }
+
+    Optional<Path> getSource() {
+        return Optional.ofNullable(mSource);
+    }
+
+    /**
+     * Represents a single WindowManager trace entry.
+     */
+    static class Entry implements ITraceEntry {
+        private final WindowManagerTraceProto mProto;
+
+        Entry(WindowManagerTraceProto proto) {
+            mProto = proto;
+        }
+
+        private static Result isWindowVisible(String windowTitle,
+                WindowTokenProto[] windowTokenProtos) {
+            boolean titleFound = false;
+            for (WindowTokenProto windowToken : windowTokenProtos) {
+                for (WindowStateProto windowState : windowToken.windows) {
+                    if (windowState.identifier.title.contains(windowTitle)) {
+                        titleFound = true;
+                        if (isVisible(windowState)) {
+                            return new Result(true /* success */,
+                                    windowState.identifier.title + " is visible");
+                        }
+                    }
+                }
+            }
+
+            String reason;
+            if (!titleFound) {
+                reason = windowTitle + " cannot be found";
+            } else {
+                reason = windowTitle + " is invisible";
+            }
+            return new Result(false /* success */, reason);
+        }
+
+        private static boolean isVisible(WindowStateProto windowState) {
+            return windowState.windowContainer.visible;
+        }
+
+        @Override
+        public long getTimestamp() {
+            return mProto.elapsedRealtimeNanos;
+        }
+
+        /**
+         * Returns window title of the top most visible app window.
+         */
+        private String getTopVisibleAppWindow() {
+            StackProto[] stacks = mProto.windowManagerService.rootWindowContainer
+                    .displays[DEFAULT_DISPLAY].stacks;
+            for (StackProto stack : stacks) {
+                for (TaskProto task : stack.tasks) {
+                    for (AppWindowTokenProto token : task.appWindowTokens) {
+                        for (WindowStateProto windowState : token.windowToken.windows) {
+                            if (windowState.windowContainer.visible) {
+                                return task.appWindowTokens[0].name;
+                            }
+                        }
+                    }
+                }
+            }
+
+            return "";
+        }
+
+        /**
+         * Checks if aboveAppWindow with {@code windowTitle} is visible.
+         */
+        Result isAboveAppWindowVisible(String windowTitle) {
+            WindowTokenProto[] windowTokenProtos = mProto.windowManagerService
+                    .rootWindowContainer
+                    .displays[DEFAULT_DISPLAY].aboveAppWindows;
+            Result result = isWindowVisible(windowTitle, windowTokenProtos);
+            return new Result(result.success, getTimestamp(), "showsAboveAppWindow", result.reason);
+        }
+
+        /**
+         * Checks if belowAppWindow with {@code windowTitle} is visible.
+         */
+        Result isBelowAppWindowVisible(String windowTitle) {
+            WindowTokenProto[] windowTokenProtos = mProto.windowManagerService
+                    .rootWindowContainer
+                    .displays[DEFAULT_DISPLAY].belowAppWindows;
+            Result result = isWindowVisible(windowTitle, windowTokenProtos);
+            return new Result(result.success, getTimestamp(), "isBelowAppWindowVisible",
+                    result.reason);
+        }
+
+        /**
+         * Checks if imeWindow with {@code windowTitle} is visible.
+         */
+        Result isImeWindowVisible(String windowTitle) {
+            WindowTokenProto[] windowTokenProtos = mProto.windowManagerService
+                    .rootWindowContainer
+                    .displays[DEFAULT_DISPLAY].imeWindows;
+            Result result = isWindowVisible(windowTitle, windowTokenProtos);
+            return new Result(result.success, getTimestamp(), "isImeWindowVisible",
+                    result.reason);
+        }
+
+        /**
+         * Checks if app window with {@code windowTitle} is on top.
+         */
+        Result isVisibleAppWindowOnTop(String windowTitle) {
+            String topAppWindow = getTopVisibleAppWindow();
+            boolean success = topAppWindow.contains(windowTitle);
+            String reason = "wanted=" + windowTitle + " found=" + topAppWindow;
+            return new Result(success, getTimestamp(), "isAppWindowOnTop", reason);
+        }
+
+        /**
+         * Checks if app window with {@code windowTitle} is visible.
+         */
+        Result isAppWindowVisible(String windowTitle) {
+            final String assertionName = "isAppWindowVisible";
+            boolean titleFound = false;
+            StackProto[] stacks = mProto.windowManagerService.rootWindowContainer
+                    .displays[DEFAULT_DISPLAY].stacks;
+            for (StackProto stack : stacks) {
+                for (TaskProto task : stack.tasks) {
+                    for (AppWindowTokenProto token : task.appWindowTokens) {
+                        if (token.name.contains(windowTitle)) {
+                            titleFound = true;
+                            for (WindowStateProto windowState : token.windowToken.windows) {
+                                if (windowState.windowContainer.visible) {
+                                    return new Result(true /* success */, getTimestamp(),
+                                            assertionName, "Window " + token.name +
+                                            "is visible");
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+            String reason;
+            if (!titleFound) {
+                reason = "Window " + windowTitle + " cannot be found";
+            } else {
+                reason = "Window " + windowTitle + " is invisible";
+            }
+            return new Result(false /* success */, getTimestamp(), assertionName, reason);
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/WindowUtils.java b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/WindowUtils.java
new file mode 100644
index 0000000..0da8761
--- /dev/null
+++ b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/WindowUtils.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2018 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.server.wm.flicker;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.support.test.InstrumentationRegistry;
+import android.view.Surface;
+import android.view.WindowManager;
+
+/**
+ * Helper functions to retrieve system window sizes and positions.
+ */
+class WindowUtils {
+
+    static Rect getDisplayBounds() {
+        Point display = new Point();
+        WindowManager wm =
+                (WindowManager) InstrumentationRegistry.getContext().getSystemService(
+                        Context.WINDOW_SERVICE);
+        wm.getDefaultDisplay().getRealSize(display);
+        return new Rect(0, 0, display.x, display.y);
+    }
+
+    private static int getCurrentRotation() {
+        WindowManager wm =
+                (WindowManager) InstrumentationRegistry.getContext().getSystemService(
+                        Context.WINDOW_SERVICE);
+        return wm.getDefaultDisplay().getRotation();
+    }
+
+    static Rect getDisplayBounds(int requestedRotation) {
+        Rect displayBounds = getDisplayBounds();
+        int currentDisplayRotation = getCurrentRotation();
+
+        boolean displayIsRotated = (currentDisplayRotation == Surface.ROTATION_90 ||
+                currentDisplayRotation == Surface.ROTATION_270);
+
+        boolean requestedDisplayIsRotated = requestedRotation == Surface.ROTATION_90 ||
+                requestedRotation == Surface.ROTATION_270;
+
+        // if the current orientation changes with the requested rotation,
+        // flip height and width of display bounds.
+        if (displayIsRotated != requestedDisplayIsRotated) {
+            return new Rect(0, 0, displayBounds.height(), displayBounds.width());
+        }
+
+        return new Rect(0, 0, displayBounds.width(), displayBounds.height());
+    }
+
+
+    static Rect getAppPosition(int requestedRotation) {
+        Rect displayBounds = getDisplayBounds();
+        int currentDisplayRotation = getCurrentRotation();
+
+        boolean displayIsRotated = currentDisplayRotation == Surface.ROTATION_90 ||
+                currentDisplayRotation == Surface.ROTATION_270;
+
+        boolean requestedAppIsRotated = requestedRotation == Surface.ROTATION_90 ||
+                requestedRotation == Surface.ROTATION_270;
+
+        // display size will change if the display is reflected. Flip height and width of app if the
+        // requested rotation is different from the current rotation.
+        if (displayIsRotated != requestedAppIsRotated) {
+            return new Rect(0, 0, displayBounds.height(), displayBounds.width());
+        }
+
+        return new Rect(0, 0, displayBounds.width(), displayBounds.height());
+    }
+
+    static Rect getStatusBarPosition(int requestedRotation) {
+        Resources resources = InstrumentationRegistry.getContext().getResources();
+        String resourceName;
+        Rect displayBounds = getDisplayBounds();
+        int width;
+        if (requestedRotation == Surface.ROTATION_0 || requestedRotation == Surface.ROTATION_180) {
+            resourceName = "status_bar_height_portrait";
+            width = Math.min(displayBounds.width(), displayBounds.height());
+        } else {
+            resourceName = "status_bar_height_landscape";
+            width = Math.max(displayBounds.width(), displayBounds.height());
+        }
+
+        int resourceId = resources.getIdentifier(resourceName, "dimen", "android");
+        int height = resources.getDimensionPixelSize(resourceId);
+
+        return new Rect(0, 0, width, height);
+    }
+
+    static Rect getNavigationBarPosition(int requestedRotation) {
+        Resources resources = InstrumentationRegistry.getContext().getResources();
+        Rect displayBounds = getDisplayBounds();
+        int displayWidth = Math.min(displayBounds.width(), displayBounds.height());
+        int displayHeight = Math.max(displayBounds.width(), displayBounds.height());
+        int resourceId;
+        if (requestedRotation == Surface.ROTATION_0 || requestedRotation == Surface.ROTATION_180) {
+            resourceId = resources.getIdentifier("navigation_bar_height", "dimen", "android");
+            int height = resources.getDimensionPixelSize(resourceId);
+            return new Rect(0, displayHeight - height, displayWidth, displayHeight);
+        } else {
+            resourceId = resources.getIdentifier("navigation_bar_width", "dimen", "android");
+            int width = resources.getDimensionPixelSize(resourceId);
+            // swap display dimensions in landscape or seascape mode
+            int temp = displayHeight;
+            displayHeight = displayWidth;
+            displayWidth = temp;
+            if (requestedRotation == Surface.ROTATION_90) {
+                return new Rect(0, 0, width, displayHeight);
+            } else {
+                return new Rect(displayWidth - width, 0, displayWidth, displayHeight);
+            }
+        }
+    }
+
+    static int getNavigationBarHeight() {
+        Resources resources = InstrumentationRegistry.getContext().getResources();
+        int resourceId = resources.getIdentifier("navigation_bar_height", "dimen", "android");
+        return resources.getDimensionPixelSize(resourceId);
+    }
+
+    static int getDockedStackDividerInset() {
+        Resources resources = InstrumentationRegistry.getContext().getResources();
+        int resourceId = resources.getIdentifier("docked_stack_divider_insets", "dimen",
+                "android");
+        return resources.getDimensionPixelSize(resourceId);
+    }
+}
diff --git a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/WmTraceSubject.java b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/WmTraceSubject.java
new file mode 100644
index 0000000..1fc7d59
--- /dev/null
+++ b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/WmTraceSubject.java
@@ -0,0 +1,193 @@
+/*
+ * Copyright (C) 2018 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.server.wm.flicker;
+
+import static com.google.common.truth.Truth.assertAbout;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import android.annotation.Nullable;
+
+import com.android.server.wm.flicker.Assertions.Result;
+import com.android.server.wm.flicker.TransitionRunner.TransitionResult;
+
+import com.google.common.truth.FailureStrategy;
+import com.google.common.truth.Subject;
+import com.google.common.truth.SubjectFactory;
+
+import java.nio.file.Path;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+/**
+ * Truth subject for {@link WindowManagerTrace} objects.
+ */
+public class WmTraceSubject extends Subject<WmTraceSubject, WindowManagerTrace> {
+    // Boiler-plate Subject.Factory for WmTraceSubject
+    private static final SubjectFactory<WmTraceSubject, WindowManagerTrace> FACTORY =
+            new SubjectFactory<WmTraceSubject, WindowManagerTrace>() {
+                @Override
+                public WmTraceSubject getSubject(
+                        FailureStrategy fs, @Nullable WindowManagerTrace target) {
+                    return new WmTraceSubject(fs, target);
+                }
+            };
+
+    private AssertionsChecker<WindowManagerTrace.Entry> mChecker = new AssertionsChecker<>();
+
+    private WmTraceSubject(FailureStrategy fs, @Nullable WindowManagerTrace subject) {
+        super(fs, subject);
+    }
+
+    // User-defined entry point
+    public static WmTraceSubject assertThat(@Nullable WindowManagerTrace entry) {
+        return assertAbout(FACTORY).that(entry);
+    }
+
+    // User-defined entry point
+    public static WmTraceSubject assertThat(@Nullable TransitionResult result) {
+        WindowManagerTrace entries = WindowManagerTrace.parseFrom(result.getWindowManagerTrace(),
+                result.getWindowManagerTracePath());
+        return assertWithMessage(result.toString()).about(FACTORY).that(entries);
+    }
+
+    // Static method for getting the subject factory (for use with assertAbout())
+    public static SubjectFactory<WmTraceSubject, WindowManagerTrace> entries() {
+        return FACTORY;
+    }
+
+    public void forAllEntries() {
+        test();
+    }
+
+    public void forRange(long startTime, long endTime) {
+        mChecker.filterByRange(startTime, endTime);
+        test();
+    }
+
+    public WmTraceSubject then() {
+        mChecker.checkChangingAssertions();
+        return this;
+    }
+
+    public void inTheBeginning() {
+        if (getSubject().getEntries().isEmpty()) {
+            fail("No entries found.");
+        }
+        mChecker.checkFirstEntry();
+        test();
+    }
+
+    public void atTheEnd() {
+        if (getSubject().getEntries().isEmpty()) {
+            fail("No entries found.");
+        }
+        mChecker.checkLastEntry();
+        test();
+    }
+
+    private void test() {
+        List<Result> failures = mChecker.test(getSubject().getEntries());
+        if (!failures.isEmpty()) {
+            Optional<Path> failureTracePath = getSubject().getSource();
+            String failureLogs = failures.stream().map(Result::toString)
+                    .collect(Collectors.joining("\n"));
+            String tracePath = "";
+            if (failureTracePath.isPresent()) {
+                tracePath = "\nWindowManager Trace can be found in: "
+                        + failureTracePath.get().toAbsolutePath() + "\n";
+            }
+            fail(tracePath + failureLogs);
+        }
+    }
+
+    public WmTraceSubject showsAboveAppWindow(String partialWindowTitle) {
+        mChecker.add(entry -> entry.isAboveAppWindowVisible(partialWindowTitle),
+                "showsAboveAppWindow(" + partialWindowTitle + ")");
+        return this;
+    }
+
+    public WmTraceSubject hidesAboveAppWindow(String partialWindowTitle) {
+        mChecker.add(entry -> entry.isAboveAppWindowVisible(partialWindowTitle).negate(),
+                "hidesAboveAppWindow" + "(" + partialWindowTitle + ")");
+        return this;
+    }
+
+    public WmTraceSubject showsBelowAppWindow(String partialWindowTitle) {
+        mChecker.add(entry -> entry.isBelowAppWindowVisible(partialWindowTitle),
+                "showsBelowAppWindow(" + partialWindowTitle + ")");
+        return this;
+    }
+
+    public WmTraceSubject hidesBelowAppWindow(String partialWindowTitle) {
+        mChecker.add(entry -> entry.isBelowAppWindowVisible(partialWindowTitle).negate(),
+                "hidesBelowAppWindow" + "(" + partialWindowTitle + ")");
+        return this;
+    }
+
+    public WmTraceSubject showsImeWindow(String partialWindowTitle) {
+        mChecker.add(entry -> entry.isImeWindowVisible(partialWindowTitle),
+                "showsBelowAppWindow(" + partialWindowTitle + ")");
+        return this;
+    }
+
+    public WmTraceSubject hidesImeWindow(String partialWindowTitle) {
+        mChecker.add(entry -> entry.isImeWindowVisible(partialWindowTitle).negate(),
+                "hidesImeWindow" + "(" + partialWindowTitle + ")");
+        return this;
+    }
+
+    public WmTraceSubject showsAppWindowOnTop(String partialWindowTitle) {
+        mChecker.add(
+                entry -> {
+                    Result result = entry.isAppWindowVisible(partialWindowTitle);
+                    if (result.passed()) {
+                        result = entry.isVisibleAppWindowOnTop(partialWindowTitle);
+                    }
+                    return result;
+                },
+                "showsAppWindowOnTop(" + partialWindowTitle + ")"
+        );
+        return this;
+    }
+
+    public WmTraceSubject hidesAppWindowOnTop(String partialWindowTitle) {
+        mChecker.add(
+                entry -> {
+                    Result result = entry.isAppWindowVisible(partialWindowTitle).negate();
+                    if (result.failed()) {
+                        result = entry.isVisibleAppWindowOnTop(partialWindowTitle).negate();
+                    }
+                    return result;
+                },
+                "hidesAppWindowOnTop(" + partialWindowTitle + ")"
+        );
+        return this;
+    }
+
+    public WmTraceSubject showsAppWindow(String partialWindowTitle) {
+        mChecker.add(entry -> entry.isAppWindowVisible(partialWindowTitle),
+                "showsAppWindow(" + partialWindowTitle + ")");
+        return this;
+    }
+
+    public WmTraceSubject hidesAppWindow(String partialWindowTitle) {
+        mChecker.add(entry -> entry.isAppWindowVisible(partialWindowTitle).negate(),
+                "hidesAppWindow(" + partialWindowTitle + ")");
+        return this;
+    }
+}
diff --git a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/ITransitionMonitor.java b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/ITransitionMonitor.java
new file mode 100644
index 0000000..67e0ecc
--- /dev/null
+++ b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/ITransitionMonitor.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2018 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.server.wm.flicker.monitor;
+
+import android.os.Environment;
+
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+/**
+ * Collects test artifacts during a UI transition.
+ */
+public interface ITransitionMonitor {
+    Path OUTPUT_DIR = Paths.get(Environment.getExternalStorageDirectory().toString(), "flicker");
+
+    /**
+     * Starts monitor.
+     */
+    void start();
+
+    /**
+     * Stops monitor.
+     */
+    void stop();
+
+    /**
+     * Saves any monitor artifacts to file adding {@code testTag} and {@code iteration}
+     * to the file name.
+     *
+     * @param testTag   suffix added to artifact name
+     * @param iteration suffix added to artifact name
+     *
+     * @return Path to saved artifact
+     */
+    default Path save(String testTag, int iteration) {
+        return save(testTag + "_" + iteration);
+    }
+
+    /**
+     * Saves any monitor artifacts to file adding {@code testTag} to the file name.
+     *
+     * @param testTag suffix added to artifact name
+     *
+     * @return Path to saved artifact
+     */
+    default Path save(String testTag) {
+        throw new UnsupportedOperationException("Save not implemented for this monitor");
+    }
+}
diff --git a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/LayersTraceMonitor.java b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/LayersTraceMonitor.java
new file mode 100644
index 0000000..c55d068
--- /dev/null
+++ b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/LayersTraceMonitor.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2018 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.server.wm.flicker.monitor;
+
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.Log;
+
+/**
+ * Captures Layers trace from SurfaceFlinger.
+ */
+public class LayersTraceMonitor extends TraceMonitor {
+    private static final String TAG = "LayersTraceMonitor";
+    private IBinder mSurfaceFlinger = ServiceManager.getService("SurfaceFlinger");
+
+    public LayersTraceMonitor() {
+        traceFileName = "layers_trace.pb";
+    }
+
+    @Override
+    public void start() {
+        setEnabled(true);
+    }
+
+    @Override
+    public void stop() {
+        setEnabled(false);
+    }
+
+    @Override
+    public boolean isEnabled() throws RemoteException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeInterfaceToken("android.ui.ISurfaceComposer");
+        mSurfaceFlinger.transact(/* LAYER_TRACE_STATUS_CODE */ 1026,
+                data, reply, 0 /* flags */);
+        return reply.readBoolean();
+    }
+
+    private void setEnabled(boolean isEnabled) {
+        Parcel data = null;
+        try {
+            if (mSurfaceFlinger != null) {
+                data = Parcel.obtain();
+                data.writeInterfaceToken("android.ui.ISurfaceComposer");
+                data.writeInt(isEnabled ? 1 : 0);
+                mSurfaceFlinger.transact( /* LAYER_TRACE_CONTROL_CODE */ 1025,
+                        data, null, 0 /* flags */);
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "Could not set layer tracing." + e.toString());
+        } finally {
+            if (data != null) {
+                data.recycle();
+            }
+        }
+    }
+}
diff --git a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/ScreenRecorder.java b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/ScreenRecorder.java
new file mode 100644
index 0000000..4787586
--- /dev/null
+++ b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/ScreenRecorder.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2018 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.server.wm.flicker.monitor;
+
+import static com.android.compatibility.common.util.SystemUtil.runShellCommand;
+
+import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
+
+import android.support.annotation.VisibleForTesting;
+import android.util.Log;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+/**
+ * Captures screen contents and saves it as a mp4 video file.
+ */
+public class ScreenRecorder implements ITransitionMonitor {
+    @VisibleForTesting
+    static final Path DEFAULT_OUTPUT_PATH = OUTPUT_DIR.resolve("transition.mp4");
+    private static final String TAG = "FLICKER";
+    private Thread recorderThread;
+
+    @VisibleForTesting
+    static Path getPath(String testTag) {
+        return OUTPUT_DIR.resolve(testTag + ".mp4");
+    }
+
+    @Override
+    public void start() {
+        OUTPUT_DIR.toFile().mkdirs();
+        String command = "screenrecord " + DEFAULT_OUTPUT_PATH;
+        recorderThread = new Thread(() -> {
+            try {
+                Runtime.getRuntime().exec(command);
+            } catch (IOException e) {
+                Log.e(TAG, "Error executing " + command, e);
+            }
+        });
+        recorderThread.start();
+    }
+
+    @Override
+    public void stop() {
+        runShellCommand("killall -s 2 screenrecord");
+        try {
+            recorderThread.join();
+        } catch (InterruptedException e) {
+            // ignore
+        }
+    }
+
+    @Override
+    public Path save(String testTag) {
+        try {
+            return Files.move(DEFAULT_OUTPUT_PATH, getPath(testTag),
+                    REPLACE_EXISTING);
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+}
diff --git a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/TraceMonitor.java b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/TraceMonitor.java
new file mode 100644
index 0000000..0e154ec
--- /dev/null
+++ b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/TraceMonitor.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2018 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.server.wm.flicker.monitor;
+
+import static com.android.compatibility.common.util.SystemUtil.runShellCommand;
+
+import android.os.RemoteException;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Locale;
+
+/**
+ * Base class for monitors containing common logic to read the trace
+ * as a byte array and save the trace to another location.
+ */
+public abstract class TraceMonitor implements ITransitionMonitor {
+    public static final String TAG = "FLICKER";
+    private static final String TRACE_DIR = "/data/misc/wmtrace/";
+
+    String traceFileName;
+
+    abstract boolean isEnabled() throws RemoteException;
+
+    /**
+     * Saves trace file to the external storage directory suffixing the name with the testtag
+     * and iteration.
+     *
+     * Moves the trace file from the default location via a shell command since the test app
+     * does not have security privileges to access /data/misc/wmtrace.
+     *
+     * @param testTag suffix added to trace name used to identify trace
+     *
+     * @return Path to saved trace file
+     */
+    @Override
+    public Path save(String testTag) {
+        OUTPUT_DIR.toFile().mkdirs();
+        Path traceFileCopy = getOutputTraceFilePath(testTag);
+        String copyCommand = String.format(Locale.getDefault(), "mv %s%s %s", TRACE_DIR,
+                traceFileName, traceFileCopy.toString());
+        runShellCommand(copyCommand);
+        return traceFileCopy;
+    }
+
+    @VisibleForTesting
+    Path getOutputTraceFilePath(String testTag) {
+        return OUTPUT_DIR.resolve(traceFileName + "_" + testTag);
+    }
+}
diff --git a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/WindowAnimationFrameStatsMonitor.java b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/WindowAnimationFrameStatsMonitor.java
new file mode 100644
index 0000000..717d187
--- /dev/null
+++ b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/WindowAnimationFrameStatsMonitor.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2018 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.server.wm.flicker.monitor;
+
+import static android.view.FrameStats.UNDEFINED_TIME_NANO;
+
+import android.app.Instrumentation;
+import android.util.Log;
+import android.view.FrameStats;
+
+/**
+ * Monitors {@link android.view.WindowAnimationFrameStats} to detect janky frames.
+ *
+ * Adapted from {@link android.support.test.jank.internal.WindowAnimationFrameStatsMonitorImpl}
+ * using the same threshold to determine jank.
+ */
+public class WindowAnimationFrameStatsMonitor implements ITransitionMonitor {
+
+    private static final String TAG = "FLICKER";
+    // Maximum normalized error in frame duration before the frame is considered janky
+    private static final double MAX_ERROR = 0.5f;
+    // Maximum normalized frame duration before the frame is considered a pause
+    private static final double PAUSE_THRESHOLD = 15.0f;
+    private Instrumentation mInstrumentation;
+    private FrameStats stats;
+    private int numJankyFrames;
+    private long mLongestFrameNano = 0L;
+
+
+    /**
+     * Constructs a WindowAnimationFrameStatsMonitor instance.
+     */
+    public WindowAnimationFrameStatsMonitor(Instrumentation instrumentation) {
+        mInstrumentation = instrumentation;
+    }
+
+    private void analyze() {
+        int frameCount = stats.getFrameCount();
+        long refreshPeriodNano = stats.getRefreshPeriodNano();
+
+        // Skip first frame
+        for (int i = 2; i < frameCount; i++) {
+            // Handle frames that have not been presented.
+            if (stats.getFramePresentedTimeNano(i) == UNDEFINED_TIME_NANO) {
+                // The animation must not have completed. Warn and break out of the loop.
+                Log.w(TAG, "Skipping fenced frame.");
+                break;
+            }
+            long frameDurationNano = stats.getFramePresentedTimeNano(i) -
+                    stats.getFramePresentedTimeNano(i - 1);
+            double normalized = (double) frameDurationNano / refreshPeriodNano;
+            if (normalized < PAUSE_THRESHOLD) {
+                if (normalized > 1.0f + MAX_ERROR) {
+                    numJankyFrames++;
+                }
+                mLongestFrameNano = Math.max(mLongestFrameNano, frameDurationNano);
+            }
+        }
+    }
+
+    @Override
+    public void start() {
+        // Clear out any previous data
+        numJankyFrames = 0;
+        mLongestFrameNano = 0;
+        mInstrumentation.getUiAutomation().clearWindowAnimationFrameStats();
+    }
+
+    @Override
+    public void stop() {
+        stats = mInstrumentation.getUiAutomation().getWindowAnimationFrameStats();
+        analyze();
+    }
+
+    public boolean jankyFramesDetected() {
+        return stats.getFrameCount() > 0 && numJankyFrames > 0;
+    }
+
+    @Override
+    public String toString() {
+        return stats.toString() +
+                " RefreshPeriodNano:" + stats.getRefreshPeriodNano() +
+                " NumJankyFrames:" + numJankyFrames +
+                " LongestFrameNano:" + mLongestFrameNano;
+    }
+}
\ No newline at end of file
diff --git a/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/WindowManagerTraceMonitor.java b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/WindowManagerTraceMonitor.java
new file mode 100644
index 0000000..ae160b68
--- /dev/null
+++ b/tests/FlickerTests/lib/src/com/android/server/wm/flicker/monitor/WindowManagerTraceMonitor.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2018 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.server.wm.flicker.monitor;
+
+import android.os.RemoteException;
+import android.view.IWindowManager;
+import android.view.WindowManagerGlobal;
+
+/**
+ * Captures WindowManager trace from WindowManager.
+ */
+public class WindowManagerTraceMonitor extends TraceMonitor {
+    private IWindowManager wm = WindowManagerGlobal.getWindowManagerService();
+
+    public WindowManagerTraceMonitor() {
+        traceFileName = "wm_trace.pb";
+    }
+
+    @Override
+    public void start() {
+        try {
+            wm.startWindowTrace();
+        } catch (RemoteException e) {
+            throw new RuntimeException("Could not start trace", e);
+        }
+    }
+
+    @Override
+    public void stop() {
+        try {
+            wm.stopWindowTrace();
+        } catch (RemoteException e) {
+            throw new RuntimeException("Could not stop trace", e);
+        }
+    }
+
+    @Override
+    public boolean isEnabled() throws RemoteException{
+        return wm.isWindowTraceEnabled();
+    }
+}
diff --git a/tests/FlickerTests/lib/test/Android.mk b/tests/FlickerTests/lib/test/Android.mk
new file mode 100644
index 0000000..0e3f58d
--- /dev/null
+++ b/tests/FlickerTests/lib/test/Android.mk
@@ -0,0 +1,36 @@
+#
+# Copyright (C) 2018 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.
+#
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+LOCAL_PACKAGE_NAME := FlickerLibTest
+LOCAL_MODULE_TAGS := tests optional
+# sign this with platform cert, so this test is allowed to call private platform apis
+LOCAL_CERTIFICATE := platform
+LOCAL_PRIVATE_PLATFORM_APIS := true
+LOCAL_COMPATIBILITY_SUITE := tests
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_JAVA_LIBRARIES := android.test.runner
+LOCAL_STATIC_JAVA_LIBRARIES := \
+    android-support-test \
+    platform-test-annotations \
+    truth-prebuilt \
+    platformprotosnano \
+    layersprotosnano \
+    flickerlib
+
+include $(BUILD_PACKAGE)
+include $(call all-makefiles-under,$(LOCAL_PATH))
\ No newline at end of file
diff --git a/tests/FlickerTests/lib/test/AndroidManifest.xml b/tests/FlickerTests/lib/test/AndroidManifest.xml
new file mode 100644
index 0000000..d30172d
--- /dev/null
+++ b/tests/FlickerTests/lib/test/AndroidManifest.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright 2018 Google Inc. All Rights Reserved.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="com.android.server.wm.flicker">
+
+    <uses-sdk android:minSdkVersion="27" android:targetSdkVersion="27"/>
+    <!-- Read and write traces from external storage -->
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+    <!-- Capture screen contents -->
+    <uses-permission android:name="android.permission.ACCESS_SURFACE_FLINGER" />
+    <!-- Run layers trace -->
+    <uses-permission android:name="android.permission.HARDWARE_TEST"/>
+    <application android:label="FlickerLibTest">
+        <uses-library android:name="android.test.runner"/>
+    </application>
+
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+                     android:targetPackage="com.android.server.wm.flicker"
+                     android:label="WindowManager Flicker Lib Test">
+    </instrumentation>
+
+</manifest>
\ No newline at end of file
diff --git a/tests/FlickerTests/lib/test/AndroidTest.xml b/tests/FlickerTests/lib/test/AndroidTest.xml
new file mode 100644
index 0000000..e4cc298
--- /dev/null
+++ b/tests/FlickerTests/lib/test/AndroidTest.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright 2018 Google Inc. All Rights Reserved.
+ -->
+<configuration description="Config for WindowManager Flicker Tests">
+    <target_preparer class="com.google.android.tradefed.targetprep.GoogleDeviceSetup">
+        <!-- keeps the screen on during tests -->
+        <option name="screen-always-on" value="on" />
+        <!-- prevents the phone from restarting -->
+        <option name="force-skip-system-props" value="true" />
+    </target_preparer>
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+        <option name="cleanup-apks" value="true"/>
+        <option name="test-file-name" value="FlickerLibTest.apk"/>
+    </target_preparer>
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+        <option name="package" value="com.android.server.wm.flicker"/>
+        <option name="hidden-api-checks" value="false" />
+    </test>
+</configuration>
diff --git a/tests/FlickerTests/lib/test/assets/testdata/layers_trace_emptyregion.pb b/tests/FlickerTests/lib/test/assets/testdata/layers_trace_emptyregion.pb
new file mode 100644
index 0000000..98ee6f3
--- /dev/null
+++ b/tests/FlickerTests/lib/test/assets/testdata/layers_trace_emptyregion.pb
Binary files differ
diff --git a/tests/FlickerTests/lib/test/assets/testdata/layers_trace_invalid_layer_visibility.pb b/tests/FlickerTests/lib/test/assets/testdata/layers_trace_invalid_layer_visibility.pb
new file mode 100644
index 0000000..20572d7
--- /dev/null
+++ b/tests/FlickerTests/lib/test/assets/testdata/layers_trace_invalid_layer_visibility.pb
Binary files differ
diff --git a/tests/FlickerTests/lib/test/assets/testdata/layers_trace_orphanlayers.pb b/tests/FlickerTests/lib/test/assets/testdata/layers_trace_orphanlayers.pb
new file mode 100644
index 0000000..af40797
--- /dev/null
+++ b/tests/FlickerTests/lib/test/assets/testdata/layers_trace_orphanlayers.pb
Binary files differ
diff --git a/tests/FlickerTests/lib/test/assets/testdata/wm_trace_openchrome.pb b/tests/FlickerTests/lib/test/assets/testdata/wm_trace_openchrome.pb
new file mode 100644
index 0000000..b3f3170
--- /dev/null
+++ b/tests/FlickerTests/lib/test/assets/testdata/wm_trace_openchrome.pb
Binary files differ
diff --git a/tests/FlickerTests/lib/test/assets/testdata/wm_trace_openchrome2.pb b/tests/FlickerTests/lib/test/assets/testdata/wm_trace_openchrome2.pb
new file mode 100644
index 0000000..b3b73ce
--- /dev/null
+++ b/tests/FlickerTests/lib/test/assets/testdata/wm_trace_openchrome2.pb
Binary files differ
diff --git a/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/AssertionsCheckerTest.java b/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/AssertionsCheckerTest.java
new file mode 100644
index 0000000..8e7fe1b
--- /dev/null
+++ b/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/AssertionsCheckerTest.java
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2018 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.server.wm.flicker;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.android.server.wm.flicker.Assertions.Result;
+
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Contains {@link AssertionsChecker} tests.
+ * To run this test: {@code atest FlickerLibTest:AssertionsCheckerTest}
+ */
+public class AssertionsCheckerTest {
+
+    /**
+     * Returns a list of SimpleEntry objects with {@code data} and incremental timestamps starting
+     * at 0.
+     */
+    private static List<SimpleEntry> getTestEntries(int... data) {
+        List<SimpleEntry> entries = new ArrayList<>();
+        for (int i = 0; i < data.length; i++) {
+            entries.add(new SimpleEntry(i, data[i]));
+        }
+        return entries;
+    }
+
+    @Test
+    public void canCheckAllEntries() {
+        AssertionsChecker<SimpleEntry> checker = new AssertionsChecker<>();
+        checker.add(SimpleEntry::isData42, "isData42");
+
+        List<Result> failures = checker.test(getTestEntries(1, 1, 1, 1, 1));
+
+        assertThat(failures).hasSize(5);
+    }
+
+    @Test
+    public void canCheckFirstEntry() {
+        AssertionsChecker<SimpleEntry> checker = new AssertionsChecker<>();
+        checker.checkFirstEntry();
+        checker.add(SimpleEntry::isData42, "isData42");
+
+        List<Result> failures = checker.test(getTestEntries(1, 1, 1, 1, 1));
+
+        assertThat(failures).hasSize(1);
+        assertThat(failures.get(0).timestamp).isEqualTo(0);
+    }
+
+    @Test
+    public void canCheckLastEntry() {
+        AssertionsChecker<SimpleEntry> checker = new AssertionsChecker<>();
+        checker.checkLastEntry();
+        checker.add(SimpleEntry::isData42, "isData42");
+
+        List<Result> failures = checker.test(getTestEntries(1, 1, 1, 1, 1));
+
+        assertThat(failures).hasSize(1);
+        assertThat(failures.get(0).timestamp).isEqualTo(4);
+    }
+
+    @Test
+    public void canCheckRangeOfEntries() {
+        AssertionsChecker<SimpleEntry> checker = new AssertionsChecker<>();
+        checker.filterByRange(1, 2);
+        checker.add(SimpleEntry::isData42, "isData42");
+
+        List<Result> failures = checker.test(getTestEntries(1, 42, 42, 1, 1));
+
+        assertThat(failures).hasSize(0);
+    }
+
+    @Test
+    public void emptyRangePasses() {
+        AssertionsChecker<SimpleEntry> checker = new AssertionsChecker<>();
+        checker.filterByRange(9, 10);
+        checker.add(SimpleEntry::isData42, "isData42");
+
+        List<Result> failures = checker.test(getTestEntries(1, 1, 1, 1, 1));
+
+        assertThat(failures).isEmpty();
+    }
+
+    @Test
+    public void canCheckChangingAssertions() {
+        AssertionsChecker<SimpleEntry> checker = new AssertionsChecker<>();
+        checker.add(SimpleEntry::isData42, "isData42");
+        checker.add(SimpleEntry::isData0, "isData0");
+        checker.checkChangingAssertions();
+
+        List<Result> failures = checker.test(getTestEntries(42, 0, 0, 0, 0));
+
+        assertThat(failures).isEmpty();
+    }
+
+    @Test
+    public void canCheckChangingAssertions_withNoAssertions() {
+        AssertionsChecker<SimpleEntry> checker = new AssertionsChecker<>();
+        checker.checkChangingAssertions();
+
+        List<Result> failures = checker.test(getTestEntries(42, 0, 0, 0, 0));
+
+        assertThat(failures).isEmpty();
+    }
+
+    @Test
+    public void canCheckChangingAssertions_withSingleAssertion() {
+        AssertionsChecker<SimpleEntry> checker = new AssertionsChecker<>();
+        checker.add(SimpleEntry::isData42, "isData42");
+        checker.checkChangingAssertions();
+
+        List<Result> failures = checker.test(getTestEntries(42, 42, 42, 42, 42));
+
+        assertThat(failures).isEmpty();
+    }
+
+    @Test
+    public void canFailCheckChangingAssertions_ifStartingAssertionFails() {
+        AssertionsChecker<SimpleEntry> checker = new AssertionsChecker<>();
+        checker.add(SimpleEntry::isData42, "isData42");
+        checker.add(SimpleEntry::isData0, "isData0");
+        checker.checkChangingAssertions();
+
+        List<Result> failures = checker.test(getTestEntries(0, 0, 0, 0, 0));
+
+        assertThat(failures).hasSize(1);
+    }
+
+    @Test
+    public void canFailCheckChangingAssertions_ifStartingAssertionAlwaysPasses() {
+        AssertionsChecker<SimpleEntry> checker = new AssertionsChecker<>();
+        checker.add(SimpleEntry::isData42, "isData42");
+        checker.add(SimpleEntry::isData0, "isData0");
+        checker.checkChangingAssertions();
+
+        List<Result> failures = checker.test(getTestEntries(0, 0, 0, 0, 0));
+
+        assertThat(failures).hasSize(1);
+    }
+
+    static class SimpleEntry implements ITraceEntry {
+        long timestamp;
+        int data;
+
+        SimpleEntry(long timestamp, int data) {
+            this.timestamp = timestamp;
+            this.data = data;
+        }
+
+        @Override
+        public long getTimestamp() {
+            return timestamp;
+        }
+
+        Result isData42() {
+            return new Result(this.data == 42, this.timestamp, "is42", "");
+        }
+
+        Result isData0() {
+            return new Result(this.data == 0, this.timestamp, "is42", "");
+        }
+    }
+}
diff --git a/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/AssertionsTest.java b/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/AssertionsTest.java
new file mode 100644
index 0000000..7fd178c
--- /dev/null
+++ b/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/AssertionsTest.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2018 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.server.wm.flicker;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.android.server.wm.flicker.Assertions.Result;
+
+import org.junit.Test;
+
+/**
+ * Contains {@link Assertions} tests.
+ * To run this test: {@code atest FlickerLibTest:AssertionsTest}
+ */
+public class AssertionsTest {
+    @Test
+    public void traceEntryAssertionCanNegateResult() {
+        Assertions.TraceAssertion<Integer> assertNumEquals42 =
+                getIntegerTraceEntryAssertion();
+
+        assertThat(assertNumEquals42.apply(1).success).isFalse();
+        assertThat(assertNumEquals42.negate().apply(1).success).isTrue();
+
+        assertThat(assertNumEquals42.apply(42).success).isTrue();
+        assertThat(assertNumEquals42.negate().apply(42).success).isFalse();
+    }
+
+    @Test
+    public void resultCanBeNegated() {
+        String reason = "Everything is fine!";
+        Result result = new Result(true, 0, "TestAssert", reason);
+        Result negatedResult = result.negate();
+        assertThat(negatedResult.success).isFalse();
+        assertThat(negatedResult.reason).isEqualTo(reason);
+        assertThat(negatedResult.assertionName).isEqualTo("!TestAssert");
+    }
+
+    private Assertions.TraceAssertion<Integer> getIntegerTraceEntryAssertion() {
+        return (num) -> {
+            if (num == 42) {
+                return new Result(true, "Num equals 42");
+            }
+            return new Result(false, "Num doesn't equal 42, actual:" + num);
+        };
+    }
+}
\ No newline at end of file
diff --git a/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/LayersTraceSubjectTest.java b/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/LayersTraceSubjectTest.java
new file mode 100644
index 0000000..d06c5d7
--- /dev/null
+++ b/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/LayersTraceSubjectTest.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2018 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.server.wm.flicker;
+
+import static com.android.server.wm.flicker.LayersTraceSubject.assertThat;
+import static com.android.server.wm.flicker.TestFileUtils.readTestFile;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.junit.Assert.fail;
+
+import android.graphics.Rect;
+
+import org.junit.Test;
+
+import java.nio.file.Paths;
+
+/**
+ * Contains {@link LayersTraceSubject} tests.
+ * To run this test: {@code atest FlickerLibTest:LayersTraceSubjectTest}
+ */
+public class LayersTraceSubjectTest {
+    private static final Rect displayRect = new Rect(0, 0, 1440, 2880);
+
+    private static LayersTrace readLayerTraceFromFile(String relativePath) {
+        try {
+            return LayersTrace.parseFrom(readTestFile(relativePath), Paths.get(relativePath));
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Test
+    public void testCanDetectEmptyRegionFromLayerTrace() {
+        LayersTrace layersTraceEntries = readLayerTraceFromFile("layers_trace_emptyregion.pb");
+        try {
+            assertThat(layersTraceEntries).coversRegion(displayRect).forAllEntries();
+            fail("Assertion passed");
+        } catch (AssertionError e) {
+            assertWithMessage("Contains path to trace")
+                    .that(e.getMessage()).contains("layers_trace_emptyregion.pb");
+            assertWithMessage("Contains timestamp")
+                    .that(e.getMessage()).contains("0h38m28s8ms");
+            assertWithMessage("Contains assertion function")
+                    .that(e.getMessage()).contains("coversRegion");
+            assertWithMessage("Contains debug info")
+                    .that(e.getMessage()).contains("Region to test: " + displayRect);
+            assertWithMessage("Contains debug info")
+                    .that(e.getMessage()).contains("first empty point: 0, 99");
+        }
+    }
+
+    @Test
+    public void testCanDetectIncorrectVisibilityFromLayerTrace() {
+        LayersTrace layersTraceEntries = readLayerTraceFromFile(
+                "layers_trace_invalid_layer_visibility.pb");
+        try {
+            assertThat(layersTraceEntries).showsLayer("com.android.server.wm.flicker.testapp")
+                    .then().hidesLayer("com.android.server.wm.flicker.testapp").forAllEntries();
+            fail("Assertion passed");
+        } catch (AssertionError e) {
+            assertWithMessage("Contains path to trace")
+                    .that(e.getMessage()).contains("layers_trace_invalid_layer_visibility.pb");
+            assertWithMessage("Contains timestamp")
+                    .that(e.getMessage()).contains("70h13m14s303ms");
+            assertWithMessage("Contains assertion function")
+                    .that(e.getMessage()).contains("!isVisible");
+            assertWithMessage("Contains debug info")
+                    .that(e.getMessage()).contains(
+                    "com.android.server.wm.flicker.testapp/com.android.server.wm.flicker.testapp"
+                            + ".SimpleActivity#0 is visible");
+        }
+    }
+}
diff --git a/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/LayersTraceTest.java b/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/LayersTraceTest.java
new file mode 100644
index 0000000..42b2aca
--- /dev/null
+++ b/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/LayersTraceTest.java
@@ -0,0 +1,229 @@
+/*
+ * Copyright (C) 2018 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.server.wm.flicker;
+
+import static com.android.server.wm.flicker.TestFileUtils.readTestFile;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.junit.Assert.fail;
+
+import android.content.Context;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.support.test.InstrumentationRegistry;
+import android.view.WindowManager;
+
+import org.junit.Test;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * Contains {@link LayersTrace} tests.
+ * To run this test: {@code atest FlickerLibTest:LayersTraceTest}
+ */
+public class LayersTraceTest {
+    private static LayersTrace readLayerTraceFromFile(String relativePath) {
+        try {
+            return LayersTrace.parseFrom(readTestFile(relativePath));
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    private static Rect getDisplayBounds() {
+        Point display = new Point();
+        WindowManager wm =
+                (WindowManager) InstrumentationRegistry.getContext().getSystemService(
+                        Context.WINDOW_SERVICE);
+        wm.getDefaultDisplay().getRealSize(display);
+        return new Rect(0, 0, display.x, display.y);
+    }
+
+    @Test
+    public void canParseAllLayers() {
+        LayersTrace trace = readLayerTraceFromFile(
+                "layers_trace_emptyregion.pb");
+        assertThat(trace.getEntries()).isNotEmpty();
+        assertThat(trace.getEntries().get(0).getTimestamp()).isEqualTo(2307984557311L);
+        assertThat(trace.getEntries().get(trace.getEntries().size() - 1).getTimestamp())
+                .isEqualTo(2308521813510L);
+        List<LayersTrace.Layer> flattenedLayers = trace.getEntries().get(0).asFlattenedLayers();
+        String msg = "Layers:\n" + flattenedLayers.stream().map(layer -> layer.mProto.name)
+                .collect(Collectors.joining("\n\t"));
+        assertWithMessage(msg).that(flattenedLayers).hasSize(47);
+    }
+
+    @Test
+    public void canParseVisibleLayers() {
+        LayersTrace trace = readLayerTraceFromFile(
+                "layers_trace_emptyregion.pb");
+        assertThat(trace.getEntries()).isNotEmpty();
+        assertThat(trace.getEntries().get(0).getTimestamp()).isEqualTo(2307984557311L);
+        assertThat(trace.getEntries().get(trace.getEntries().size() - 1).getTimestamp())
+                .isEqualTo(2308521813510L);
+        List<LayersTrace.Layer> flattenedLayers = trace.getEntries().get(0).asFlattenedLayers();
+        List<LayersTrace.Layer> visibleLayers = flattenedLayers.stream()
+                .filter(layer -> layer.isVisible() && !layer.isHiddenByParent())
+                .collect(Collectors.toList());
+
+        String msg = "Visible Layers:\n" + visibleLayers.stream()
+                .map(layer -> layer.mProto.name)
+                .collect(Collectors.joining("\n\t"));
+
+        assertWithMessage(msg).that(visibleLayers).hasSize(9);
+    }
+
+    @Test
+    public void canParseLayerHierarchy() {
+        LayersTrace trace = readLayerTraceFromFile(
+                "layers_trace_emptyregion.pb");
+        assertThat(trace.getEntries()).isNotEmpty();
+        assertThat(trace.getEntries().get(0).getTimestamp()).isEqualTo(2307984557311L);
+        assertThat(trace.getEntries().get(trace.getEntries().size() - 1).getTimestamp())
+                .isEqualTo(2308521813510L);
+        List<LayersTrace.Layer> layers = trace.getEntries().get(0).getRootLayers();
+        assertThat(layers).hasSize(2);
+        assertThat(layers.get(0).mChildren).hasSize(layers.get(0).mProto.children.length);
+        assertThat(layers.get(1).mChildren).hasSize(layers.get(1).mProto.children.length);
+    }
+
+    // b/76099859
+    @Test
+    public void canDetectOrphanLayers() {
+        try {
+            readLayerTraceFromFile(
+                    "layers_trace_orphanlayers.pb");
+            fail("Failed to detect orphaned layers.");
+        } catch (RuntimeException exception) {
+            assertThat(exception.getMessage()).contains(
+                    "Failed to parse layers trace. Found orphan layers "
+                            + "with parent layer id:1006 : 49");
+        }
+    }
+
+    // b/75276931
+    @Test
+    public void canDetectUncoveredRegion() {
+        LayersTrace trace = readLayerTraceFromFile(
+                "layers_trace_emptyregion.pb");
+        LayersTrace.Entry entry = trace.getEntry(2308008331271L);
+
+        Assertions.Result result = entry.coversRegion(getDisplayBounds());
+
+        assertThat(result.failed()).isTrue();
+        assertThat(result.reason).contains("Region to test: Rect(0, 0 - 1440, 2880)");
+        assertThat(result.reason).contains("first empty point: 0, 99");
+        assertThat(result.reason).contains("visible regions:");
+        assertWithMessage("Reason contains list of visible regions")
+                .that(result.reason).contains("StatusBar#0Rect(0, 0 - 1440, 98");
+    }
+
+    // Visible region tests
+    @Test
+    public void canTestLayerVisibleRegion_layerDoesNotExist() {
+        LayersTrace trace = readLayerTraceFromFile(
+                "layers_trace_emptyregion.pb");
+        LayersTrace.Entry entry = trace.getEntry(2308008331271L);
+
+        final Rect expectedVisibleRegion = new Rect(0, 0, 1, 1);
+        Assertions.Result result = entry.hasVisibleRegion("ImaginaryLayer",
+                expectedVisibleRegion);
+
+        assertThat(result.failed()).isTrue();
+        assertThat(result.reason).contains("Could not find ImaginaryLayer");
+    }
+
+    @Test
+    public void canTestLayerVisibleRegion_layerDoesNotHaveExpectedVisibleRegion() {
+        LayersTrace trace = readLayerTraceFromFile(
+                "layers_trace_emptyregion.pb");
+        LayersTrace.Entry entry = trace.getEntry(2307993020072L);
+
+        final Rect expectedVisibleRegion = new Rect(0, 0, 1, 1);
+        Assertions.Result result = entry.hasVisibleRegion("NexusLauncherActivity#2",
+                expectedVisibleRegion);
+
+        assertThat(result.failed()).isTrue();
+        assertThat(result.reason).contains(
+                "Layer com.google.android.apps.nexuslauncher/com.google.android.apps"
+                        + ".nexuslauncher.NexusLauncherActivity#2 is invisible: activeBuffer=null"
+                        + " type != ColorLayer flags=1 (FLAG_HIDDEN set) visible region is empty");
+    }
+
+    @Test
+    public void canTestLayerVisibleRegion_layerIsHiddenByParent() {
+        LayersTrace trace = readLayerTraceFromFile(
+                "layers_trace_emptyregion.pb");
+        LayersTrace.Entry entry = trace.getEntry(2308455948035L);
+
+        final Rect expectedVisibleRegion = new Rect(0, 0, 1, 1);
+        Assertions.Result result = entry.hasVisibleRegion(
+                "SurfaceView - com.android.chrome/com.google.android.apps.chrome.Main",
+                expectedVisibleRegion);
+
+        assertThat(result.failed()).isTrue();
+        assertThat(result.reason).contains(
+                "Layer SurfaceView - com.android.chrome/com.google.android.apps.chrome.Main#0 is "
+                        + "hidden by parent: com.android.chrome/com.google.android.apps.chrome"
+                        + ".Main#0");
+    }
+
+    @Test
+    public void canTestLayerVisibleRegion_incorrectRegionSize() {
+        LayersTrace trace = readLayerTraceFromFile(
+                "layers_trace_emptyregion.pb");
+        LayersTrace.Entry entry = trace.getEntry(2308008331271L);
+
+        final Rect expectedVisibleRegion = new Rect(0, 0, 1440, 99);
+        Assertions.Result result = entry.hasVisibleRegion(
+                "StatusBar",
+                expectedVisibleRegion);
+
+        assertThat(result.failed()).isTrue();
+        assertThat(result.reason).contains("StatusBar#0 has visible "
+                + "region:Rect(0, 0 - 1440, 98) expected:Rect(0, 0 - 1440, 99)");
+    }
+
+    @Test
+    public void canTestLayerVisibleRegion() {
+        LayersTrace trace = readLayerTraceFromFile(
+                "layers_trace_emptyregion.pb");
+        LayersTrace.Entry entry = trace.getEntry(2308008331271L);
+
+        final Rect expectedVisibleRegion = new Rect(0, 0, 1440, 98);
+        Assertions.Result result = entry.hasVisibleRegion("StatusBar", expectedVisibleRegion);
+
+        assertThat(result.passed()).isTrue();
+    }
+
+    @Test
+    public void canTestLayerVisibleRegion_layerIsNotVisible() {
+        LayersTrace trace = readLayerTraceFromFile(
+                "layers_trace_invalid_layer_visibility.pb");
+        LayersTrace.Entry entry = trace.getEntry(252794268378458L);
+
+        Assertions.Result result = entry.isVisible("com.android.server.wm.flicker.testapp");
+        assertThat(result.failed()).isTrue();
+        assertThat(result.reason).contains(
+                "Layer com.android.server.wm.flicker.testapp/com.android.server.wm.flicker"
+                        + ".testapp.SimpleActivity#0 is invisible: type != ColorLayer visible "
+                        + "region is empty");
+    }
+}
diff --git a/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/TestFileUtils.java b/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/TestFileUtils.java
new file mode 100644
index 0000000..5a24e6d
--- /dev/null
+++ b/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/TestFileUtils.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2018 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.server.wm.flicker;
+
+import android.content.Context;
+import android.support.test.InstrumentationRegistry;
+
+import com.google.common.io.ByteStreams;
+
+import java.io.InputStream;
+
+/**
+ * Helper functions for test file resources.
+ */
+class TestFileUtils {
+    static byte[] readTestFile(String relativePath) throws Exception {
+        Context context = InstrumentationRegistry.getContext();
+        InputStream in = context.getResources().getAssets().open("testdata/" + relativePath);
+        return ByteStreams.toByteArray(in);
+    }
+}
diff --git a/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/TransitionRunnerTest.java b/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/TransitionRunnerTest.java
new file mode 100644
index 0000000..9c5e2059a
--- /dev/null
+++ b/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/TransitionRunnerTest.java
@@ -0,0 +1,258 @@
+/*
+ * Copyright (C) 2018 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.server.wm.flicker;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+
+import android.os.Environment;
+
+import com.android.server.wm.flicker.TransitionRunner.TransitionBuilder;
+import com.android.server.wm.flicker.TransitionRunner.TransitionResult;
+import com.android.server.wm.flicker.monitor.LayersTraceMonitor;
+import com.android.server.wm.flicker.monitor.ScreenRecorder;
+import com.android.server.wm.flicker.monitor.WindowAnimationFrameStatsMonitor;
+import com.android.server.wm.flicker.monitor.WindowManagerTraceMonitor;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.InOrder;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.io.IOException;
+import java.nio.file.Paths;
+import java.util.List;
+
+/**
+ * Contains {@link TransitionRunner} tests.
+ * {@code atest FlickerLibTest:TransitionRunnerTest}
+ */
+public class TransitionRunnerTest {
+    @Mock
+    private SimpleUiTransitions mTransitionsMock;
+    @Mock
+    private ScreenRecorder mScreenRecorderMock;
+    @Mock
+    private WindowManagerTraceMonitor mWindowManagerTraceMonitorMock;
+    @Mock
+    private LayersTraceMonitor mLayersTraceMonitorMock;
+    @Mock
+    private WindowAnimationFrameStatsMonitor mWindowAnimationFrameStatsMonitor;
+    @InjectMocks
+    private TransitionBuilder mTransitionBuilder;
+
+    @Before
+    public void init() {
+        MockitoAnnotations.initMocks(this);
+    }
+
+    @Test
+    public void transitionsRunInOrder() {
+        TransitionRunner.newBuilder()
+                .runBeforeAll(mTransitionsMock::turnOnDevice)
+                .runBefore(mTransitionsMock::openApp)
+                .run(mTransitionsMock::performMagic)
+                .runAfter(mTransitionsMock::closeApp)
+                .runAfterAll(mTransitionsMock::cleanUpTracks)
+                .skipLayersTrace()
+                .skipWindowManagerTrace()
+                .build()
+                .run();
+
+        InOrder orderVerifier = inOrder(mTransitionsMock);
+        orderVerifier.verify(mTransitionsMock).turnOnDevice();
+        orderVerifier.verify(mTransitionsMock).openApp();
+        orderVerifier.verify(mTransitionsMock).performMagic();
+        orderVerifier.verify(mTransitionsMock).closeApp();
+        orderVerifier.verify(mTransitionsMock).cleanUpTracks();
+    }
+
+    @Test
+    public void canCombineTransitions() {
+        TransitionRunner.newBuilder()
+                .runBeforeAll(mTransitionsMock::turnOnDevice)
+                .runBeforeAll(mTransitionsMock::turnOnDevice)
+                .runBefore(mTransitionsMock::openApp)
+                .runBefore(mTransitionsMock::openApp)
+                .run(mTransitionsMock::performMagic)
+                .run(mTransitionsMock::performMagic)
+                .runAfter(mTransitionsMock::closeApp)
+                .runAfter(mTransitionsMock::closeApp)
+                .runAfterAll(mTransitionsMock::cleanUpTracks)
+                .runAfterAll(mTransitionsMock::cleanUpTracks)
+                .skipLayersTrace()
+                .skipWindowManagerTrace()
+                .build()
+                .run();
+
+        final int wantedNumberOfInvocations = 2;
+        verify(mTransitionsMock, times(wantedNumberOfInvocations)).turnOnDevice();
+        verify(mTransitionsMock, times(wantedNumberOfInvocations)).openApp();
+        verify(mTransitionsMock, times(wantedNumberOfInvocations)).performMagic();
+        verify(mTransitionsMock, times(wantedNumberOfInvocations)).closeApp();
+        verify(mTransitionsMock, times(wantedNumberOfInvocations)).cleanUpTracks();
+    }
+
+    @Test
+    public void emptyTransitionPasses() {
+        List<TransitionResult> results = TransitionRunner.newBuilder()
+                .skipLayersTrace()
+                .skipWindowManagerTrace()
+                .build()
+                .run()
+                .getResults();
+        assertThat(results).hasSize(1);
+        assertThat(results.get(0).layersTraceExists()).isFalse();
+        assertThat(results.get(0).windowManagerTraceExists()).isFalse();
+        assertThat(results.get(0).screenCaptureVideoExists()).isFalse();
+    }
+
+    @Test
+    public void canRepeatTransitions() {
+        final int wantedNumberOfInvocations = 10;
+        TransitionRunner.newBuilder()
+                .runBeforeAll(mTransitionsMock::turnOnDevice)
+                .runBefore(mTransitionsMock::openApp)
+                .run(mTransitionsMock::performMagic)
+                .runAfter(mTransitionsMock::closeApp)
+                .runAfterAll(mTransitionsMock::cleanUpTracks)
+                .repeat(wantedNumberOfInvocations)
+                .skipLayersTrace()
+                .skipWindowManagerTrace()
+                .build()
+                .run();
+        verify(mTransitionsMock).turnOnDevice();
+        verify(mTransitionsMock, times(wantedNumberOfInvocations)).openApp();
+        verify(mTransitionsMock, times(wantedNumberOfInvocations)).performMagic();
+        verify(mTransitionsMock, times(wantedNumberOfInvocations)).closeApp();
+        verify(mTransitionsMock).cleanUpTracks();
+    }
+
+    private void emptyTask() {
+
+    }
+
+    @Test
+    public void canCaptureWindowManagerTrace() {
+        mTransitionBuilder
+                .run(this::emptyTask)
+                .includeJankyRuns()
+                .skipLayersTrace()
+                .withTag("mCaptureWmTraceTransitionRunner")
+                .build().run();
+        InOrder orderVerifier = inOrder(mWindowManagerTraceMonitorMock);
+        orderVerifier.verify(mWindowManagerTraceMonitorMock).start();
+        orderVerifier.verify(mWindowManagerTraceMonitorMock).stop();
+        orderVerifier.verify(mWindowManagerTraceMonitorMock)
+                .save("mCaptureWmTraceTransitionRunner", 0);
+        verifyNoMoreInteractions(mWindowManagerTraceMonitorMock);
+    }
+
+    @Test
+    public void canCaptureLayersTrace() {
+        mTransitionBuilder
+                .run(this::emptyTask)
+                .includeJankyRuns()
+                .skipWindowManagerTrace()
+                .withTag("mCaptureLayersTraceTransitionRunner")
+                .build().run();
+        InOrder orderVerifier = inOrder(mLayersTraceMonitorMock);
+        orderVerifier.verify(mLayersTraceMonitorMock).start();
+        orderVerifier.verify(mLayersTraceMonitorMock).stop();
+        orderVerifier.verify(mLayersTraceMonitorMock)
+                .save("mCaptureLayersTraceTransitionRunner", 0);
+        verifyNoMoreInteractions(mLayersTraceMonitorMock);
+    }
+
+    @Test
+    public void canRecordEachRun() throws IOException {
+        mTransitionBuilder
+                .run(this::emptyTask)
+                .withTag("mRecordEachRun")
+                .recordEachRun()
+                .includeJankyRuns()
+                .skipLayersTrace()
+                .skipWindowManagerTrace()
+                .repeat(2)
+                .build().run();
+        InOrder orderVerifier = inOrder(mScreenRecorderMock);
+        orderVerifier.verify(mScreenRecorderMock).start();
+        orderVerifier.verify(mScreenRecorderMock).stop();
+        orderVerifier.verify(mScreenRecorderMock).save("mRecordEachRun", 0);
+        orderVerifier.verify(mScreenRecorderMock).start();
+        orderVerifier.verify(mScreenRecorderMock).stop();
+        orderVerifier.verify(mScreenRecorderMock).save("mRecordEachRun", 1);
+        verifyNoMoreInteractions(mScreenRecorderMock);
+    }
+
+    @Test
+    public void canRecordAllRuns() throws IOException {
+        doReturn(Paths.get(Environment.getExternalStorageDirectory().getAbsolutePath(),
+                "mRecordAllRuns.mp4")).when(mScreenRecorderMock).save("mRecordAllRuns");
+        mTransitionBuilder
+                .run(this::emptyTask)
+                .recordAllRuns()
+                .includeJankyRuns()
+                .skipLayersTrace()
+                .skipWindowManagerTrace()
+                .withTag("mRecordAllRuns")
+                .repeat(2)
+                .build().run();
+        InOrder orderVerifier = inOrder(mScreenRecorderMock);
+        orderVerifier.verify(mScreenRecorderMock).start();
+        orderVerifier.verify(mScreenRecorderMock).stop();
+        orderVerifier.verify(mScreenRecorderMock).save("mRecordAllRuns");
+        verifyNoMoreInteractions(mScreenRecorderMock);
+    }
+
+    @Test
+    public void canSkipJankyRuns() {
+        doReturn(false).doReturn(true).doReturn(false)
+                .when(mWindowAnimationFrameStatsMonitor).jankyFramesDetected();
+        List<TransitionResult> results = mTransitionBuilder
+                .run(this::emptyTask)
+                .skipLayersTrace()
+                .skipWindowManagerTrace()
+                .repeat(3)
+                .build().run().getResults();
+        assertThat(results).hasSize(2);
+    }
+
+    public static class SimpleUiTransitions {
+        public void turnOnDevice() {
+        }
+
+        public void openApp() {
+        }
+
+        public void performMagic() {
+        }
+
+        public void closeApp() {
+        }
+
+        public void cleanUpTracks() {
+        }
+    }
+}
diff --git a/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/WindowManagerTraceTest.java b/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/WindowManagerTraceTest.java
new file mode 100644
index 0000000..4927871
--- /dev/null
+++ b/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/WindowManagerTraceTest.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2018 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.server.wm.flicker;
+
+import static com.android.server.wm.flicker.TestFileUtils.readTestFile;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.android.server.wm.flicker.Assertions.Result;
+
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Contains {@link WindowManagerTrace} tests.
+ * To run this test: {@code atest FlickerLibTest:WindowManagerTraceTest}
+ */
+public class WindowManagerTraceTest {
+    private WindowManagerTrace mTrace;
+
+    private static WindowManagerTrace readWindowManagerTraceFromFile(String relativePath) {
+        try {
+            return WindowManagerTrace.parseFrom(readTestFile(relativePath));
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Before
+    public void setup() {
+        mTrace = readWindowManagerTraceFromFile("wm_trace_openchrome.pb");
+    }
+
+    @Test
+    public void canParseAllEntries() {
+        assertThat(mTrace.getEntries().get(0).getTimestamp()).isEqualTo(241777211939236L);
+        assertThat(mTrace.getEntries().get(mTrace.getEntries().size() - 1).getTimestamp()).isEqualTo
+                (241779809471942L);
+    }
+
+    @Test
+    public void canDetectAboveAppWindowVisibility() {
+        WindowManagerTrace.Entry entry = mTrace.getEntry(241777211939236L);
+        Result result = entry.isAboveAppWindowVisible("NavigationBar");
+        assertThat(result.passed()).isTrue();
+    }
+
+    @Test
+    public void canDetectBelowAppWindowVisibility() {
+        WindowManagerTrace.Entry entry = mTrace.getEntry(241777211939236L);
+        Result result = entry.isBelowAppWindowVisible("wallpaper");
+        assertThat(result.passed()).isTrue();
+    }
+
+    @Test
+    public void canDetectAppWindowVisibility() {
+        WindowManagerTrace.Entry entry = mTrace.getEntry(241777211939236L);
+        Result result = entry.isAppWindowVisible("com.google.android.apps.nexuslauncher");
+        assertThat(result.passed()).isTrue();
+    }
+
+    @Test
+    public void canFailWithReasonForVisibilityChecks_windowNotFound() {
+        WindowManagerTrace.Entry entry = mTrace.getEntry(241777211939236L);
+        Result result = entry.isAboveAppWindowVisible("ImaginaryWindow");
+        assertThat(result.failed()).isTrue();
+        assertThat(result.reason).contains("ImaginaryWindow cannot be found");
+    }
+
+    @Test
+    public void canFailWithReasonForVisibilityChecks_windowNotVisible() {
+        WindowManagerTrace.Entry entry = mTrace.getEntry(241777211939236L);
+        Result result = entry.isAboveAppWindowVisible("AssistPreviewPanel");
+        assertThat(result.failed()).isTrue();
+        assertThat(result.reason).contains("AssistPreviewPanel is invisible");
+    }
+
+    @Test
+    public void canDetectAppZOrder() {
+        WindowManagerTrace.Entry entry = mTrace.getEntry(241778130296410L);
+        Result result = entry.isVisibleAppWindowOnTop("com.google.android.apps.chrome");
+        assertThat(result.passed()).isTrue();
+    }
+
+    @Test
+    public void canFailWithReasonForZOrderChecks_windowNotOnTop() {
+        WindowManagerTrace.Entry entry = mTrace.getEntry(241778130296410L);
+        Result result = entry.isVisibleAppWindowOnTop("com.google.android.apps.nexuslauncher");
+        assertThat(result.failed()).isTrue();
+        assertThat(result.reason).contains("wanted=com.google.android.apps.nexuslauncher");
+        assertThat(result.reason).contains("found=com.android.chrome/"
+                + "com.google.android.apps.chrome.Main");
+    }
+}
diff --git a/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/WmTraceSubjectTest.java b/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/WmTraceSubjectTest.java
new file mode 100644
index 0000000..d547a18
--- /dev/null
+++ b/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/WmTraceSubjectTest.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2018 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.server.wm.flicker;
+
+import static com.android.server.wm.flicker.TestFileUtils.readTestFile;
+import static com.android.server.wm.flicker.WmTraceSubject.assertThat;
+
+import org.junit.Test;
+
+/**
+ * Contains {@link WmTraceSubject} tests.
+ * To run this test: {@code atest FlickerLibTest:WmTraceSubjectTest}
+ */
+public class WmTraceSubjectTest {
+    private static WindowManagerTrace readWmTraceFromFile(String relativePath) {
+        try {
+            return WindowManagerTrace.parseFrom(readTestFile(relativePath));
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Test
+    public void testCanTransitionInAppWindow() {
+        WindowManagerTrace trace = readWmTraceFromFile("wm_trace_openchrome2.pb");
+
+        assertThat(trace).showsAppWindowOnTop("com.google.android.apps.nexuslauncher/"
+                + ".NexusLauncherActivity").forRange(174684850717208L, 174685957511016L);
+        assertThat(trace).showsAppWindowOnTop(
+                "com.google.android.apps.nexuslauncher/.NexusLauncherActivity")
+                .then()
+                .showsAppWindowOnTop("com.android.chrome")
+                .forAllEntries();
+    }
+}
diff --git a/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/monitor/LayersTraceMonitorTest.java b/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/monitor/LayersTraceMonitorTest.java
new file mode 100644
index 0000000..dbd6761
--- /dev/null
+++ b/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/monitor/LayersTraceMonitorTest.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2018 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.server.wm.flicker.monitor;
+
+import static android.surfaceflinger.nano.Layerstrace.LayersTraceFileProto.MAGIC_NUMBER_H;
+import static android.surfaceflinger.nano.Layerstrace.LayersTraceFileProto.MAGIC_NUMBER_L;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.surfaceflinger.nano.Layerstrace.LayersTraceFileProto;
+
+import com.google.common.io.Files;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.File;
+
+/**
+ * Contains {@link LayersTraceMonitor} tests.
+ * To run this test: {@code atest FlickerLibTest:LayersTraceMonitorTest}
+ */
+public class LayersTraceMonitorTest {
+    private LayersTraceMonitor mLayersTraceMonitor;
+
+    @Before
+    public void setup() {
+        mLayersTraceMonitor = new LayersTraceMonitor();
+    }
+
+    @After
+    public void teardown() {
+        mLayersTraceMonitor.stop();
+        mLayersTraceMonitor.getOutputTraceFilePath("captureLayersTrace").toFile().delete();
+    }
+
+    @Test
+    public void canStartLayersTrace() throws Exception {
+        mLayersTraceMonitor.start();
+        assertThat(mLayersTraceMonitor.isEnabled()).isTrue();
+    }
+
+    @Test
+    public void canStopLayersTrace() throws Exception {
+        mLayersTraceMonitor.start();
+        assertThat(mLayersTraceMonitor.isEnabled()).isTrue();
+        mLayersTraceMonitor.stop();
+        assertThat(mLayersTraceMonitor.isEnabled()).isFalse();
+    }
+
+    @Test
+    public void captureLayersTrace() throws Exception {
+        mLayersTraceMonitor.start();
+        mLayersTraceMonitor.stop();
+        File testFile = mLayersTraceMonitor.save("captureLayersTrace").toFile();
+        assertThat(testFile.exists()).isTrue();
+        byte[] trace = Files.toByteArray(testFile);
+        assertThat(trace.length).isGreaterThan(0);
+        LayersTraceFileProto mLayerTraceFileProto = LayersTraceFileProto.parseFrom(trace);
+        assertThat(mLayerTraceFileProto.magicNumber).isEqualTo(
+                (long) MAGIC_NUMBER_H << 32 | MAGIC_NUMBER_L);
+    }
+}
diff --git a/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/monitor/ScreenRecorderTest.java b/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/monitor/ScreenRecorderTest.java
new file mode 100644
index 0000000..e73eecc
--- /dev/null
+++ b/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/monitor/ScreenRecorderTest.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2018 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.server.wm.flicker.monitor;
+
+import static android.os.SystemClock.sleep;
+
+import static com.android.server.wm.flicker.monitor.ScreenRecorder.DEFAULT_OUTPUT_PATH;
+import static com.android.server.wm.flicker.monitor.ScreenRecorder.getPath;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.File;
+import java.io.IOException;
+
+/**
+ * Contains {@link ScreenRecorder} tests.
+ * To run this test: {@code atest FlickerLibTest:ScreenRecorderTest}
+ */
+public class ScreenRecorderTest {
+    private static final String TEST_VIDEO_FILENAME = "test.mp4";
+    private ScreenRecorder mScreenRecorder;
+
+    @Before
+    public void setup() {
+        mScreenRecorder = new ScreenRecorder();
+    }
+
+    @After
+    public void teardown() {
+        DEFAULT_OUTPUT_PATH.toFile().delete();
+        getPath(TEST_VIDEO_FILENAME).toFile().delete();
+    }
+
+    @Test
+    public void videoIsRecorded() {
+        mScreenRecorder.start();
+        sleep(100);
+        mScreenRecorder.stop();
+        File file = DEFAULT_OUTPUT_PATH.toFile();
+        assertThat(file.exists()).isTrue();
+    }
+
+    @Test
+    public void videoCanBeSaved() {
+        mScreenRecorder.start();
+        sleep(100);
+        mScreenRecorder.stop();
+        mScreenRecorder.save(TEST_VIDEO_FILENAME);
+        File file = getPath(TEST_VIDEO_FILENAME).toFile();
+        assertThat(file.exists()).isTrue();
+    }
+}
diff --git a/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/monitor/WindowAnimationFrameStatsMonitorTest.java b/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/monitor/WindowAnimationFrameStatsMonitorTest.java
new file mode 100644
index 0000000..f7fa0d5
--- /dev/null
+++ b/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/monitor/WindowAnimationFrameStatsMonitorTest.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2018 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.server.wm.flicker.monitor;
+
+import static com.android.server.wm.flicker.AutomationUtils.wakeUpAndGoToHomeScreen;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.support.test.InstrumentationRegistry;
+
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+
+/**
+ * Contains {@link WindowAnimationFrameStatsMonitor} tests.
+ * To run this test: {@code atest FlickerLibTest:WindowAnimationFrameStatsMonitorTest}
+ */
+public class WindowAnimationFrameStatsMonitorTest {
+    private WindowAnimationFrameStatsMonitor mWindowAnimationFrameStatsMonitor;
+
+    @Before
+    public void setup() {
+        mWindowAnimationFrameStatsMonitor = new WindowAnimationFrameStatsMonitor(
+                InstrumentationRegistry.getInstrumentation());
+        wakeUpAndGoToHomeScreen();
+    }
+
+    // TODO(vishnun)
+    @Ignore("Disabled until app-helper libraries are available.")
+    @Test
+    public void captureWindowAnimationFrameStats() throws Exception {
+        mWindowAnimationFrameStatsMonitor.start();
+        //AppHelperWrapper.getInstance().getHelper(CHROME).open();
+        //AppHelperWrapper.getInstance().getHelper(CHROME).exit();
+        mWindowAnimationFrameStatsMonitor.stop();
+    }
+}
diff --git a/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/monitor/WindowManagerTraceMonitorTest.java b/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/monitor/WindowManagerTraceMonitorTest.java
new file mode 100644
index 0000000..56284d7
--- /dev/null
+++ b/tests/FlickerTests/lib/test/src/com/android/server/wm/flicker/monitor/WindowManagerTraceMonitorTest.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2018 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.server.wm.flicker.monitor;
+
+import static com.android.server.wm.nano.WindowManagerTraceFileProto.MAGIC_NUMBER_H;
+import static com.android.server.wm.nano.WindowManagerTraceFileProto.MAGIC_NUMBER_L;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.android.server.wm.nano.WindowManagerTraceFileProto;
+
+import com.google.common.io.Files;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.File;
+
+/**
+ * Contains {@link WindowManagerTraceMonitor} tests.
+ * To run this test: {@code atest FlickerLibTest:WindowManagerTraceMonitorTest}
+ */
+public class WindowManagerTraceMonitorTest {
+    private WindowManagerTraceMonitor mWindowManagerTraceMonitor;
+
+    @Before
+    public void setup() {
+        mWindowManagerTraceMonitor = new WindowManagerTraceMonitor();
+    }
+
+    @After
+    public void teardown() {
+        mWindowManagerTraceMonitor.stop();
+        mWindowManagerTraceMonitor.getOutputTraceFilePath("captureWindowTrace").toFile().delete();
+    }
+
+    @Test
+    public void canStartWindowTrace() throws Exception {
+        mWindowManagerTraceMonitor.start();
+        assertThat(mWindowManagerTraceMonitor.isEnabled()).isTrue();
+    }
+
+    @Test
+    public void canStopWindowTrace() throws Exception {
+        mWindowManagerTraceMonitor.start();
+        assertThat(mWindowManagerTraceMonitor.isEnabled()).isTrue();
+        mWindowManagerTraceMonitor.stop();
+        assertThat(mWindowManagerTraceMonitor.isEnabled()).isFalse();
+    }
+
+    @Test
+    public void captureWindowTrace() throws Exception {
+        mWindowManagerTraceMonitor.start();
+        mWindowManagerTraceMonitor.stop();
+        File testFile = mWindowManagerTraceMonitor.save("captureWindowTrace").toFile();
+        assertThat(testFile.exists()).isTrue();
+        byte[] trace = Files.toByteArray(testFile);
+        assertThat(trace.length).isGreaterThan(0);
+        WindowManagerTraceFileProto mWindowTraceFileProto = WindowManagerTraceFileProto.parseFrom(
+                trace);
+        assertThat(mWindowTraceFileProto.magicNumber).isEqualTo(
+                (long) MAGIC_NUMBER_H << 32 | MAGIC_NUMBER_L);
+    }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ChangeAppRotationTest.java b/tests/FlickerTests/src/com/android/server/wm/flicker/ChangeAppRotationTest.java
new file mode 100644
index 0000000..34f4ebb
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ChangeAppRotationTest.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2018 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.server.wm.flicker;
+
+import static android.view.Surface.rotationToString;
+
+import static com.android.server.wm.flicker.CommonTransitions.changeAppRotation;
+import static com.android.server.wm.flicker.WindowUtils.getAppPosition;
+import static com.android.server.wm.flicker.WindowUtils.getNavigationBarPosition;
+import static com.android.server.wm.flicker.WindowUtils.getStatusBarPosition;
+import static com.android.server.wm.flicker.WmTraceSubject.assertThat;
+
+import android.graphics.Rect;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.LargeTest;
+import android.util.Log;
+import android.view.Surface;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+/**
+ * Cycle through supported app rotations.
+ * To run this test: {@code atest FlickerTest:ChangeAppRotationTest}
+ */
+@RunWith(Parameterized.class)
+@LargeTest
+public class ChangeAppRotationTest extends FlickerTestBase {
+    private int beginRotation;
+    private int endRotation;
+
+    public ChangeAppRotationTest(String beginRotationName, String endRotationName,
+            int beginRotation, int endRotation) {
+        this.testApp = new StandardAppHelper(InstrumentationRegistry.getInstrumentation(),
+                "com.android.server.wm.flicker.testapp", "SimpleApp");
+        this.beginRotation = beginRotation;
+        this.endRotation = endRotation;
+    }
+
+    @Parameters(name = "{0}-{1}")
+    public static Collection<Object[]> getParams() {
+        int[] supportedRotations =
+                {Surface.ROTATION_0, Surface.ROTATION_90, Surface.ROTATION_270};
+        Collection<Object[]> params = new ArrayList<>();
+        for (int begin : supportedRotations) {
+            for (int end : supportedRotations) {
+                if (begin != end) {
+                    params.add(new Object[]{rotationToString(begin), rotationToString(end), begin,
+                            end});
+                }
+            }
+        }
+        return params;
+    }
+
+    @Before
+    public void runTransition() {
+        super.runTransition(
+                changeAppRotation(testApp, uiDevice, beginRotation, endRotation).build());
+    }
+
+    @Test
+    public void checkVisibility_navBarWindowIsAlwaysVisible() {
+        checkResults(result -> assertThat(result)
+                .showsAboveAppWindow(NAVIGATION_BAR_WINDOW_TITLE).forAllEntries());
+    }
+
+    @Test
+    public void checkVisibility_statusBarWindowIsAlwaysVisible() {
+        checkResults(result -> assertThat(result)
+                .showsAboveAppWindow(STATUS_BAR_WINDOW_TITLE).forAllEntries());
+    }
+
+    @Test
+    public void checkPosition_navBarLayerRotatesAndScales() {
+        Rect startingPos = getNavigationBarPosition(beginRotation);
+        Rect endingPos = getNavigationBarPosition(endRotation);
+        checkResults(result -> {
+                    LayersTraceSubject.assertThat(result)
+                            .hasVisibleRegion(NAVIGATION_BAR_WINDOW_TITLE, startingPos)
+                            .inTheBeginning();
+                    LayersTraceSubject.assertThat(result)
+                            .hasVisibleRegion(NAVIGATION_BAR_WINDOW_TITLE, endingPos).atTheEnd();
+                }
+        );
+    }
+
+    @Test
+    public void checkPosition_appLayerRotates() {
+        Rect startingPos = getAppPosition(beginRotation);
+        Rect endingPos = getAppPosition(endRotation);
+        Log.e(TAG, "startingPos=" + startingPos + " endingPos=" + endingPos);
+        checkResults(result -> {
+                    LayersTraceSubject.assertThat(result)
+                            .hasVisibleRegion(testApp.getPackage(), startingPos).inTheBeginning();
+                    LayersTraceSubject.assertThat(result)
+                            .hasVisibleRegion(testApp.getPackage(), endingPos).atTheEnd();
+                }
+        );
+    }
+
+    @Test
+    public void checkPosition_statusBarLayerScales() {
+        Rect startingPos = getStatusBarPosition(beginRotation);
+        Rect endingPos = getStatusBarPosition(endRotation);
+        checkResults(result -> {
+                    LayersTraceSubject.assertThat(result)
+                            .hasVisibleRegion(STATUS_BAR_WINDOW_TITLE, startingPos)
+                            .inTheBeginning();
+                    LayersTraceSubject.assertThat(result)
+                            .hasVisibleRegion(STATUS_BAR_WINDOW_TITLE, endingPos).atTheEnd();
+                }
+        );
+    }
+
+    @Test
+    public void checkVisibility_navBarLayerIsAlwaysVisible() {
+        checkResults(result -> LayersTraceSubject.assertThat(result)
+                .showsLayer(NAVIGATION_BAR_WINDOW_TITLE).forAllEntries());
+    }
+
+    @Test
+    public void checkVisibility_statusBarLayerIsAlwaysVisible() {
+        checkResults(result -> LayersTraceSubject.assertThat(result)
+                .showsLayer(STATUS_BAR_WINDOW_TITLE).forAllEntries());
+    }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/CloseImeWindowToAppTest.java b/tests/FlickerTests/src/com/android/server/wm/flicker/CloseImeWindowToAppTest.java
new file mode 100644
index 0000000..2b62fcf
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/CloseImeWindowToAppTest.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2018 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.server.wm.flicker;
+
+import static com.android.server.wm.flicker.CommonTransitions.editTextLoseFocusToApp;
+import static com.android.server.wm.flicker.WindowUtils.getDisplayBounds;
+
+import android.platform.helpers.IAppHelper;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.LargeTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test IME window closing back to app window transitions.
+ * To run this test: {@code atest FlickerTests:CloseImeWindowToAppTest}
+ */
+@LargeTest
+@RunWith(AndroidJUnit4.class)
+public class CloseImeWindowToAppTest extends FlickerTestBase {
+
+    private static final String IME_WINDOW_TITLE = "InputMethod";
+    private IAppHelper mImeTestApp = new StandardAppHelper(
+            InstrumentationRegistry.getInstrumentation(),
+            "com.android.server.wm.flicker.testapp", "ImeApp");
+
+    @Before
+    public void runTransition() {
+        super.runTransition(editTextLoseFocusToApp(uiDevice)
+                .includeJankyRuns().build());
+    }
+
+    @Test
+    public void checkVisibility_imeLayerBecomesInvisible() {
+        checkResults(result -> LayersTraceSubject.assertThat(result)
+                .showsLayer(IME_WINDOW_TITLE)
+                .then()
+                .hidesLayer(IME_WINDOW_TITLE)
+                .forAllEntries());
+    }
+
+    @Test
+    public void checkVisibility_imeAppLayerIsAlwaysVisible() {
+        checkResults(result -> LayersTraceSubject.assertThat(result)
+                .showsLayer(mImeTestApp.getPackage())
+                .forAllEntries());
+    }
+
+    @Test
+    public void checkVisibility_imeAppWindowIsAlwaysVisible() {
+        checkResults(result -> WmTraceSubject.assertThat(result)
+                .showsAppWindowOnTop(mImeTestApp.getPackage())
+                .forAllEntries());
+    }
+
+    @Test
+    public void checkCoveredRegion_noUncoveredRegions() {
+        checkResults(result -> LayersTraceSubject.assertThat(result).coversRegion(
+                getDisplayBounds()).forAllEntries());
+    }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/CloseImeWindowToHomeTest.java b/tests/FlickerTests/src/com/android/server/wm/flicker/CloseImeWindowToHomeTest.java
new file mode 100644
index 0000000..42b161f
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/CloseImeWindowToHomeTest.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2018 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.server.wm.flicker;
+
+import static com.android.server.wm.flicker.CommonTransitions.editTextLoseFocusToHome;
+import static com.android.server.wm.flicker.WindowUtils.getDisplayBounds;
+
+import android.platform.helpers.IAppHelper;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.LargeTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test IME window closing to home transitions.
+ * To run this test: {@code atest FlickerTests:CloseImeWindowToHomeTest}
+ */
+@LargeTest
+@RunWith(AndroidJUnit4.class)
+public class CloseImeWindowToHomeTest extends FlickerTestBase {
+
+    private static final String IME_WINDOW_TITLE = "InputMethod";
+    private IAppHelper mImeTestApp = new StandardAppHelper(
+            InstrumentationRegistry.getInstrumentation(),
+            "com.android.server.wm.flicker.testapp", "ImeApp");
+
+    @Before
+    public void runTransition() {
+        super.runTransition(editTextLoseFocusToHome(uiDevice)
+                .includeJankyRuns().build());
+    }
+
+    @Test
+    public void checkVisibility_imeWindowBecomesInvisible() {
+        checkResults(result -> WmTraceSubject.assertThat(result)
+                .showsImeWindow(IME_WINDOW_TITLE)
+                .then()
+                .hidesImeWindow(IME_WINDOW_TITLE)
+                .forAllEntries());
+    }
+
+    @Test
+    public void checkVisibility_imeLayerBecomesInvisible() {
+        checkResults(result -> LayersTraceSubject.assertThat(result)
+                .showsLayer(IME_WINDOW_TITLE)
+                .then()
+                .hidesLayer(IME_WINDOW_TITLE)
+                .forAllEntries());
+    }
+
+    @Test
+    public void checkVisibility_imeAppLayerBecomesInvisible() {
+        checkResults(result -> LayersTraceSubject.assertThat(result)
+                .showsLayer(mImeTestApp.getPackage())
+                .then()
+                .hidesLayer(mImeTestApp.getPackage())
+                .forAllEntries());
+    }
+
+    @Test
+    public void checkVisibility_imeAppWindowBecomesInvisible() {
+        checkResults(result -> WmTraceSubject.assertThat(result)
+                .showsAppWindowOnTop(mImeTestApp.getPackage())
+                .then()
+                .hidesAppWindowOnTop(mImeTestApp.getPackage())
+                .forAllEntries());
+    }
+
+    @Test
+    public void checkCoveredRegion_noUncoveredRegions() {
+        checkResults(result -> LayersTraceSubject.assertThat(result).coversRegion(
+                getDisplayBounds()).forAllEntries());
+    }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonTransitions.java b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonTransitions.java
new file mode 100644
index 0000000..92bb1ea
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonTransitions.java
@@ -0,0 +1,317 @@
+/*
+ * Copyright (C) 2018 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.server.wm.flicker;
+
+import static android.os.SystemClock.sleep;
+import static android.view.Surface.rotationToString;
+
+import static com.android.server.wm.flicker.AutomationUtils.clearRecents;
+import static com.android.server.wm.flicker.AutomationUtils.closePipWindow;
+import static com.android.server.wm.flicker.AutomationUtils.exitSplitScreen;
+import static com.android.server.wm.flicker.AutomationUtils.expandPipWindow;
+import static com.android.server.wm.flicker.AutomationUtils.launchSplitScreen;
+import static com.android.server.wm.flicker.AutomationUtils.stopPackage;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.RemoteException;
+import android.platform.helpers.IAppHelper;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.uiautomator.By;
+import android.support.test.uiautomator.UiDevice;
+import android.support.test.uiautomator.UiObject2;
+import android.support.test.uiautomator.Until;
+import android.util.Rational;
+import android.view.Surface;
+
+import com.android.server.wm.flicker.TransitionRunner.TransitionBuilder;
+
+/**
+ * Collection of common transitions which can be used to test different apps or scenarios.
+ */
+class CommonTransitions {
+
+    public static final int ITERATIONS = 1;
+    private static final String TAG = "FLICKER";
+    private static final long APP_LAUNCH_TIMEOUT = 10000;
+
+    private static void setRotation(UiDevice device, int rotation) {
+        try {
+            switch (rotation) {
+                case Surface.ROTATION_270:
+                    device.setOrientationLeft();
+                    break;
+
+                case Surface.ROTATION_90:
+                    device.setOrientationRight();
+                    break;
+
+                case Surface.ROTATION_0:
+                default:
+                    device.setOrientationNatural();
+            }
+            // Wait for animation to complete
+            sleep(3000);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    private static void clickEditTextWidget(UiDevice device, IAppHelper testApp) {
+        UiObject2 editText = device.findObject(By.res(testApp.getPackage(), "plain_text_input"));
+        editText.click();
+        sleep(500);
+    }
+
+    private static void clickEnterPipButton(UiDevice device, IAppHelper testApp) {
+        UiObject2 enterPipButton = device.findObject(By.res(testApp.getPackage(), "enter_pip"));
+        enterPipButton.click();
+        sleep(500);
+    }
+
+    static TransitionBuilder openAppWarm(IAppHelper testApp, UiDevice
+            device) {
+        return TransitionRunner.newBuilder()
+                .withTag("OpenAppWarm_" + testApp.getLauncherName())
+                .runBeforeAll(AutomationUtils::wakeUpAndGoToHomeScreen)
+                .runBeforeAll(testApp::open)
+                .runBefore(device::pressHome)
+                .runBefore(device::waitForIdle)
+                .run(testApp::open)
+                .runAfterAll(testApp::exit)
+                .runAfterAll(AutomationUtils::setDefaultWait)
+                .repeat(ITERATIONS);
+    }
+
+    static TransitionBuilder closeAppWithBackKey(IAppHelper testApp, UiDevice
+            device) {
+        return TransitionRunner.newBuilder()
+                .withTag("closeAppWithBackKey_" + testApp.getLauncherName())
+                .runBeforeAll(AutomationUtils::wakeUpAndGoToHomeScreen)
+                .runBefore(testApp::open)
+                .runBefore(device::waitForIdle)
+                .run(device::pressBack)
+                .run(device::waitForIdle)
+                .runAfterAll(testApp::exit)
+                .runAfterAll(AutomationUtils::setDefaultWait)
+                .repeat(ITERATIONS);
+    }
+
+    static TransitionBuilder closeAppWithHomeKey(IAppHelper testApp, UiDevice
+            device) {
+        return TransitionRunner.newBuilder()
+                .withTag("closeAppWithHomeKey_" + testApp.getLauncherName())
+                .runBeforeAll(AutomationUtils::wakeUpAndGoToHomeScreen)
+                .runBefore(testApp::open)
+                .runBefore(device::waitForIdle)
+                .run(device::pressHome)
+                .run(device::waitForIdle)
+                .runAfterAll(testApp::exit)
+                .runAfterAll(AutomationUtils::setDefaultWait)
+                .repeat(ITERATIONS);
+    }
+
+    static TransitionBuilder getOpenAppCold(IAppHelper testApp,
+            UiDevice device) {
+        return TransitionRunner.newBuilder()
+                .withTag("OpenAppCold_" + testApp.getLauncherName())
+                .runBeforeAll(AutomationUtils::wakeUpAndGoToHomeScreen)
+                .runBefore(device::pressHome)
+                .runBefore(testApp::exit)
+                .runBefore(device::waitForIdle)
+                .run(testApp::open)
+                .runAfterAll(testApp::exit)
+                .repeat(ITERATIONS);
+    }
+
+    static TransitionBuilder changeAppRotation(IAppHelper testApp, UiDevice
+            device, int beginRotation, int endRotation) {
+        return TransitionRunner.newBuilder()
+                .withTag("changeAppRotation_" + testApp.getLauncherName()
+                        + rotationToString(beginRotation) + "_" +
+                        rotationToString(endRotation))
+                .runBeforeAll(AutomationUtils::wakeUpAndGoToHomeScreen)
+                .runBeforeAll(testApp::open)
+                .runBefore(() -> setRotation(device, beginRotation))
+                .run(() -> setRotation(device, endRotation))
+                .runAfterAll(testApp::exit)
+                .runAfterAll(() -> setRotation(device, Surface.ROTATION_0))
+                .repeat(ITERATIONS);
+    }
+
+    static TransitionBuilder changeAppRotation(Intent intent, String intentId, Context context,
+            UiDevice
+                    device, int beginRotation, int endRotation) {
+        final String testTag = "changeAppRotation_" + intentId + "_" +
+                rotationToString(beginRotation) + "_" + rotationToString(endRotation);
+        return TransitionRunner.newBuilder()
+                .withTag(testTag)
+                .runBeforeAll(AutomationUtils::wakeUpAndGoToHomeScreen)
+                .runBeforeAll(() -> {
+                            context.startActivity(intent);
+                            device.wait(Until.hasObject(By.pkg(intent.getComponent()
+                                        .getPackageName()).depth(0)), APP_LAUNCH_TIMEOUT);
+                        }
+                )
+                .runBefore(() -> setRotation(device, beginRotation))
+                .run(() -> setRotation(device, endRotation))
+                .runAfterAll(() -> stopPackage(context, intent.getComponent().getPackageName()))
+                .runAfterAll(() -> setRotation(device, Surface.ROTATION_0))
+                .repeat(ITERATIONS);
+    }
+
+    static TransitionBuilder appToSplitScreen(IAppHelper testApp, UiDevice device) {
+        return TransitionRunner.newBuilder()
+                .withTag("appToSplitScreen_" + testApp.getLauncherName())
+                .runBeforeAll(AutomationUtils::wakeUpAndGoToHomeScreen)
+                .runBefore(testApp::open)
+                .runBefore(device::waitForIdle)
+                .runBefore(() -> sleep(500))
+                .run(() -> launchSplitScreen(device))
+                .runAfter(() -> exitSplitScreen(device))
+                .runAfterAll(testApp::exit)
+                .repeat(ITERATIONS);
+    }
+
+    static TransitionBuilder splitScreenToLauncher(IAppHelper testApp, UiDevice device) {
+        return TransitionRunner.newBuilder()
+                .withTag("splitScreenToLauncher_" + testApp.getLauncherName())
+                .runBeforeAll(AutomationUtils::wakeUpAndGoToHomeScreen)
+                .runBefore(testApp::open)
+                .runBefore(device::waitForIdle)
+                .runBefore(() -> launchSplitScreen(device))
+                .run(() -> exitSplitScreen(device))
+                .runAfterAll(testApp::exit)
+                .repeat(ITERATIONS);
+    }
+
+    static TransitionBuilder editTextSetFocus(UiDevice device) {
+        IAppHelper testApp = new StandardAppHelper(InstrumentationRegistry.getInstrumentation(),
+                "com.android.server.wm.flicker.testapp", "ImeApp");
+        return TransitionRunner.newBuilder()
+                .withTag("editTextSetFocus_" + testApp.getLauncherName())
+                .runBeforeAll(AutomationUtils::wakeUpAndGoToHomeScreen)
+                .runBefore(device::pressHome)
+                .runBefore(testApp::open)
+                .run(() -> clickEditTextWidget(device, testApp))
+                .runAfterAll(testApp::exit)
+                .repeat(ITERATIONS);
+    }
+
+    static TransitionBuilder resizeSplitScreen(IAppHelper testAppTop, IAppHelper testAppBottom,
+            UiDevice device, Rational startRatio, Rational stopRatio) {
+        String testTag = "resizeSplitScreen_" + testAppTop.getLauncherName() + "_" +
+                testAppBottom.getLauncherName() + "_" +
+                startRatio.toString().replace("/", ":") + "_to_" +
+                stopRatio.toString().replace("/", ":");
+        return TransitionRunner.newBuilder()
+                .withTag(testTag)
+                .runBeforeAll(AutomationUtils::wakeUpAndGoToHomeScreen)
+                .runBeforeAll(() -> clearRecents(device))
+                .runBefore(testAppBottom::open)
+                .runBefore(device::pressHome)
+                .runBefore(testAppTop::open)
+                .runBefore(device::waitForIdle)
+                .runBefore(() -> launchSplitScreen(device))
+                .runBefore(() -> {
+                    UiObject2 snapshot = device.findObject(
+                            By.res("com.google.android.apps.nexuslauncher", "snapshot"));
+                    snapshot.click();
+                })
+                .runBefore(() -> AutomationUtils.resizeSplitScreen(device, startRatio))
+                .run(() -> AutomationUtils.resizeSplitScreen(device, stopRatio))
+                .runAfter(() -> exitSplitScreen(device))
+                .runAfter(device::pressHome)
+                .runAfterAll(testAppTop::exit)
+                .runAfterAll(testAppBottom::exit)
+                .repeat(ITERATIONS);
+    }
+
+    static TransitionBuilder editTextLoseFocusToHome(UiDevice device) {
+        IAppHelper testApp = new StandardAppHelper(InstrumentationRegistry.getInstrumentation(),
+                "com.android.server.wm.flicker.testapp", "ImeApp");
+        return TransitionRunner.newBuilder()
+                .withTag("editTextLoseFocusToHome_" + testApp.getLauncherName())
+                .runBeforeAll(AutomationUtils::wakeUpAndGoToHomeScreen)
+                .runBefore(device::pressHome)
+                .runBefore(testApp::open)
+                .runBefore(() -> clickEditTextWidget(device, testApp))
+                .run(device::pressHome)
+                .run(device::waitForIdle)
+                .runAfterAll(testApp::exit)
+                .repeat(ITERATIONS);
+    }
+
+    static TransitionBuilder editTextLoseFocusToApp(UiDevice device) {
+        IAppHelper testApp = new StandardAppHelper(InstrumentationRegistry.getInstrumentation(),
+                "com.android.server.wm.flicker.testapp", "ImeApp");
+        return TransitionRunner.newBuilder()
+                .withTag("editTextLoseFocusToApp_" + testApp.getLauncherName())
+                .runBeforeAll(AutomationUtils::wakeUpAndGoToHomeScreen)
+                .runBefore(device::pressHome)
+                .runBefore(testApp::open)
+                .runBefore(() -> clickEditTextWidget(device, testApp))
+                .run(device::pressBack)
+                .run(device::waitForIdle)
+                .runAfterAll(testApp::exit)
+                .repeat(ITERATIONS);
+    }
+
+    static TransitionBuilder enterPipMode(UiDevice device) {
+        IAppHelper testApp = new StandardAppHelper(InstrumentationRegistry.getInstrumentation(),
+                "com.android.server.wm.flicker.testapp", "PipApp");
+        return TransitionRunner.newBuilder()
+                .withTag("enterPipMode_" + testApp.getLauncherName())
+                .runBeforeAll(AutomationUtils::wakeUpAndGoToHomeScreen)
+                .runBefore(device::pressHome)
+                .runBefore(testApp::open)
+                .run(() -> clickEnterPipButton(device, testApp))
+                .runAfter(() -> closePipWindow(device))
+                .runAfterAll(testApp::exit)
+                .repeat(ITERATIONS);
+    }
+
+    static TransitionBuilder exitPipModeToHome(UiDevice device) {
+        IAppHelper testApp = new StandardAppHelper(InstrumentationRegistry.getInstrumentation(),
+                "com.android.server.wm.flicker.testapp", "PipApp");
+        return TransitionRunner.newBuilder()
+                .withTag("exitPipModeToHome_" + testApp.getLauncherName())
+                .runBeforeAll(AutomationUtils::wakeUpAndGoToHomeScreen)
+                .runBefore(device::pressHome)
+                .runBefore(testApp::open)
+                .runBefore(() -> clickEnterPipButton(device, testApp))
+                .run(() -> closePipWindow(device))
+                .run(device::waitForIdle)
+                .runAfterAll(testApp::exit)
+                .repeat(ITERATIONS);
+    }
+
+    static TransitionBuilder exitPipModeToApp(UiDevice device) {
+        IAppHelper testApp = new StandardAppHelper(InstrumentationRegistry.getInstrumentation(),
+                "com.android.server.wm.flicker.testapp", "PipApp");
+        return TransitionRunner.newBuilder()
+                .withTag("exitPipModeToApp_" + testApp.getLauncherName())
+                .runBeforeAll(AutomationUtils::wakeUpAndGoToHomeScreen)
+                .runBefore(device::pressHome)
+                .runBefore(testApp::open)
+                .runBefore(() -> clickEnterPipButton(device, testApp))
+                .run(() -> expandPipWindow(device))
+                .run(device::waitForIdle)
+                .runAfterAll(testApp::exit)
+                .repeat(ITERATIONS);
+    }
+}
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/DebugTest.java b/tests/FlickerTests/src/com/android/server/wm/flicker/DebugTest.java
new file mode 100644
index 0000000..fec248c
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/DebugTest.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2018 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.server.wm.flicker;
+
+import android.platform.helpers.IAppHelper;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.test.uiautomator.UiDevice;
+import android.util.Rational;
+import android.view.Surface;
+
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests to help debug individual transitions, capture video recordings and create test cases.
+ */
+@Ignore("Used for debugging transitions used in FlickerTests.")
+@RunWith(AndroidJUnit4.class)
+public class DebugTest {
+    private IAppHelper testApp = new StandardAppHelper(InstrumentationRegistry.getInstrumentation(),
+            "com.android.server.wm.flicker.testapp", "SimpleApp");
+    private UiDevice uiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
+
+    /**
+     * atest FlickerTest:DebugTests#openAppCold
+     */
+    @Test
+    public void openAppCold() {
+        CommonTransitions.getOpenAppCold(testApp, uiDevice).recordAllRuns().build().run();
+    }
+
+    /**
+     * atest FlickerTest:DebugTests#openAppWarm
+     */
+    @Test
+    public void openAppWarm() {
+        CommonTransitions.openAppWarm(testApp, uiDevice).recordAllRuns().build().run();
+    }
+
+    /**
+     * atest FlickerTest:DebugTests#changeOrientationFromNaturalToLeft
+     */
+    @Test
+    public void changeOrientationFromNaturalToLeft() {
+        CommonTransitions.changeAppRotation(testApp, uiDevice, Surface.ROTATION_0,
+                Surface.ROTATION_270).recordAllRuns().build().run();
+    }
+
+    /**
+     * atest FlickerTest:DebugTests#closeAppWithBackKey
+     */
+    @Test
+    public void closeAppWithBackKey() {
+        CommonTransitions.closeAppWithBackKey(testApp, uiDevice).recordAllRuns().build().run();
+    }
+
+    /**
+     * atest FlickerTest:DebugTests#closeAppWithHomeKey
+     */
+    @Test
+    public void closeAppWithHomeKey() {
+        CommonTransitions.closeAppWithHomeKey(testApp, uiDevice).recordAllRuns().build().run();
+    }
+
+    /**
+     * atest FlickerTest:DebugTests#openAppToSplitScreen
+     */
+    @Test
+    public void openAppToSplitScreen() {
+        CommonTransitions.appToSplitScreen(testApp, uiDevice).includeJankyRuns().recordAllRuns()
+                .build().run();
+    }
+
+    /**
+     * atest FlickerTest:DebugTests#splitScreenToLauncher
+     */
+    @Test
+    public void splitScreenToLauncher() {
+        CommonTransitions.splitScreenToLauncher(testApp,
+                uiDevice).includeJankyRuns().recordAllRuns()
+                .build().run();
+    }
+
+    /**
+     * atest FlickerTest:DebugTests#resizeSplitScreen
+     */
+    @Test
+    public void resizeSplitScreen() {
+        IAppHelper bottomApp = new StandardAppHelper(InstrumentationRegistry.getInstrumentation(),
+                "com.android.server.wm.flicker.testapp", "ImeApp");
+        CommonTransitions.resizeSplitScreen(testApp, bottomApp, uiDevice, new Rational(1, 3),
+                new Rational(2, 3)).includeJankyRuns().recordEachRun().build().run();
+    }
+
+    // IME tests
+
+    /**
+     * atest FlickerTest:DebugTests#editTextSetFocus
+     */
+    @Test
+    public void editTextSetFocus() {
+        CommonTransitions.editTextSetFocus(uiDevice).includeJankyRuns().recordEachRun()
+                .build().run();
+    }
+
+    /**
+     * atest FlickerTest:DebugTests#editTextLoseFocusToHome
+     */
+    @Test
+    public void editTextLoseFocusToHome() {
+        CommonTransitions.editTextLoseFocusToHome(uiDevice).includeJankyRuns().recordEachRun()
+                .build().run();
+    }
+
+    /**
+     * atest FlickerTest:DebugTests#editTextLoseFocusToApp
+     */
+    @Test
+    public void editTextLoseFocusToApp() {
+        CommonTransitions.editTextLoseFocusToHome(uiDevice).includeJankyRuns().recordEachRun()
+                .build().run();
+    }
+
+    // PIP tests
+
+    /**
+     * atest FlickerTest:DebugTests#enterPipMode
+     */
+    @Test
+    public void enterPipMode() {
+        CommonTransitions.enterPipMode(uiDevice).includeJankyRuns().recordEachRun().build().run();
+    }
+
+    /**
+     * atest FlickerTest:DebugTests#exitPipModeToHome
+     */
+    @Test
+    public void exitPipModeToHome() {
+        CommonTransitions.exitPipModeToHome(uiDevice).includeJankyRuns().recordEachRun()
+                .build().run();
+    }
+
+    /**
+     * atest FlickerTest:DebugTests#exitPipModeToApp
+     */
+    @Test
+    public void exitPipModeToApp() {
+        CommonTransitions.exitPipModeToApp(uiDevice).includeJankyRuns().recordEachRun()
+                .build().run();
+    }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/FlickerTestBase.java b/tests/FlickerTests/src/com/android/server/wm/flicker/FlickerTestBase.java
new file mode 100644
index 0000000..7061b23
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/FlickerTestBase.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2018 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.server.wm.flicker;
+
+import static com.android.server.wm.flicker.AutomationUtils.setDefaultWait;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import android.platform.helpers.IAppHelper;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.uiautomator.UiDevice;
+import android.util.Log;
+
+import com.android.server.wm.flicker.TransitionRunner.TransitionResult;
+
+import org.junit.After;
+import org.junit.AfterClass;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.function.Consumer;
+
+/**
+ * Base class of all Flicker test that performs common functions for all flicker tests:
+ * <p>
+ * - Caches transitions so that a transition is run once and the transition results are used by
+ * tests multiple times. This is needed for parameterized tests which call the BeforeClass methods
+ * multiple times.
+ * - Keeps track of all test artifacts and deletes ones which do not need to be reviewed.
+ * - Fails tests if results are not available for any test due to jank.
+ */
+public class FlickerTestBase {
+    public static final String TAG = "FLICKER";
+    static final String NAVIGATION_BAR_WINDOW_TITLE = "NavigationBar";
+    static final String STATUS_BAR_WINDOW_TITLE = "StatusBar";
+    static final String DOCKED_STACK_DIVIDER = "DockedStackDivider";
+    private static HashMap<String, List<TransitionResult>> transitionResults =
+            new HashMap<>();
+    IAppHelper testApp;
+    UiDevice uiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
+    private List<TransitionResult> results;
+    private TransitionResult lastResult = null;
+
+    /**
+     * Teardown any system settings and clean up test artifacts from the file system.
+     *
+     * Note: test artifacts for failed tests will remain on the device.
+     */
+    @AfterClass
+    public static void teardown() {
+        setDefaultWait();
+        transitionResults.values().stream()
+                .flatMap(List::stream)
+                .forEach(result -> {
+                    if (result.canDelete()) {
+                        result.delete();
+                    } else {
+                        if (result.layersTraceExists()) {
+                            Log.e(TAG, "Layers trace saved to " + result.getLayersTracePath());
+                        }
+                        if (result.windowManagerTraceExists()) {
+                            Log.e(TAG, "WindowManager trace saved to " + result
+                                    .getWindowManagerTracePath
+                                            ());
+                        }
+                        if (result.screenCaptureVideoExists()) {
+                            Log.e(TAG, "Screen capture video saved to " + result
+                                    .screenCaptureVideo.toString());
+                        }
+                    }
+                });
+    }
+
+    /**
+     * Runs a transition, returns a cached result if the transition has run before.
+     */
+    void runTransition(TransitionRunner transition) {
+        if (transitionResults.containsKey(transition.getTestTag())) {
+            results = transitionResults.get(transition.getTestTag());
+            return;
+        }
+        results = transition.run().getResults();
+        /* Fail if we don't have any results due to jank */
+        assertWithMessage("No results to test because all transition runs were invalid because "
+                + "of Jank").that(results).isNotEmpty();
+        transitionResults.put(transition.getTestTag(), results);
+    }
+
+    /**
+     * Goes through a list of transition results and checks assertions on each result.
+     */
+    void checkResults(Consumer<TransitionResult> assertion) {
+
+        for (TransitionResult result : results) {
+            lastResult = result;
+            assertion.accept(result);
+        }
+        lastResult = null;
+    }
+
+    /**
+     * Kludge to mark a file for saving. If {@code checkResults} fails, the last result is not
+     * cleared. This indicates the assertion failed for the result, so mark it for saving.
+     */
+    @After
+    public void markArtifactsForSaving() {
+        if (lastResult != null) {
+            lastResult.flagForSaving();
+        }
+    }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/OpenAppColdTest.java b/tests/FlickerTests/src/com/android/server/wm/flicker/OpenAppColdTest.java
new file mode 100644
index 0000000..7e71369
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/OpenAppColdTest.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2018 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.server.wm.flicker;
+
+import static com.android.server.wm.flicker.CommonTransitions.getOpenAppCold;
+import static com.android.server.wm.flicker.WindowUtils.getDisplayBounds;
+import static com.android.server.wm.flicker.WmTraceSubject.assertThat;
+
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.LargeTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test cold launch app from launcher.
+ * To run this test: {@code atest FlickerTests:OpenAppColdTest}
+ */
+@LargeTest
+@RunWith(AndroidJUnit4.class)
+public class OpenAppColdTest extends FlickerTestBase {
+
+    public OpenAppColdTest() {
+        this.testApp = new StandardAppHelper(InstrumentationRegistry.getInstrumentation(),
+                "com.android.server.wm.flicker.testapp", "SimpleApp");
+    }
+
+    @Before
+    public void runTransition() {
+        super.runTransition(getOpenAppCold(testApp, uiDevice).build());
+    }
+
+    @Test
+    public void checkVisibility_navBarWindowIsAlwaysVisible() {
+        checkResults(result -> assertThat(result)
+                .showsAboveAppWindow(NAVIGATION_BAR_WINDOW_TITLE).forAllEntries());
+    }
+
+    @Test
+    public void checkVisibility_statusBarWindowIsAlwaysVisible() {
+        checkResults(result -> assertThat(result)
+                .showsAboveAppWindow(STATUS_BAR_WINDOW_TITLE).forAllEntries());
+    }
+
+    @Test
+    public void checkVisibility_wallpaperWindowBecomesInvisible() {
+        checkResults(result -> assertThat(result)
+                .showsBelowAppWindow("wallpaper")
+                .then()
+                .hidesBelowAppWindow("wallpaper")
+                .forAllEntries());
+    }
+
+    @Test
+    public void checkZOrder_appWindowReplacesLauncherAsTopWindow() {
+        checkResults(result -> assertThat(result)
+                .showsAppWindowOnTop(
+                        "com.google.android.apps.nexuslauncher/.NexusLauncherActivity")
+                .then()
+                .showsAppWindowOnTop(testApp.getPackage())
+                .forAllEntries());
+    }
+
+    @Test
+    public void checkCoveredRegion_noUncoveredRegions() {
+        checkResults(result -> LayersTraceSubject.assertThat(result).coversRegion(
+                getDisplayBounds()).forAllEntries());
+    }
+
+    @Test
+    public void checkVisibility_navBarLayerIsAlwaysVisible() {
+        checkResults(result -> LayersTraceSubject.assertThat(result)
+                .showsLayer(NAVIGATION_BAR_WINDOW_TITLE).forAllEntries());
+    }
+
+    @Test
+    public void checkVisibility_statusBarLayerIsAlwaysVisible() {
+        checkResults(result -> LayersTraceSubject.assertThat(result)
+                .showsLayer(STATUS_BAR_WINDOW_TITLE).forAllEntries());
+    }
+
+    @Test
+    public void checkVisibility_wallpaperLayerBecomesInvisible() {
+        checkResults(result -> LayersTraceSubject.assertThat(result)
+                .showsLayer("wallpaper")
+                .then()
+                .hidesLayer("wallpaper")
+                .forAllEntries());
+    }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/OpenAppToSplitScreenTest.java b/tests/FlickerTests/src/com/android/server/wm/flicker/OpenAppToSplitScreenTest.java
new file mode 100644
index 0000000..745569a
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/OpenAppToSplitScreenTest.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2018 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.server.wm.flicker;
+
+import static com.android.server.wm.flicker.CommonTransitions.appToSplitScreen;
+import static com.android.server.wm.flicker.WindowUtils.getDisplayBounds;
+
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.LargeTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test open app to split screen.
+ * To run this test: {@code atest FlickerTests:OpenAppToSplitScreenTest}
+ */
+@LargeTest
+@RunWith(AndroidJUnit4.class)
+public class OpenAppToSplitScreenTest extends FlickerTestBase {
+
+    public OpenAppToSplitScreenTest() {
+        this.testApp = new StandardAppHelper(InstrumentationRegistry.getInstrumentation(),
+                "com.android.server.wm.flicker.testapp", "SimpleApp");
+    }
+
+    @Before
+    public void runTransition() {
+        super.runTransition(appToSplitScreen(testApp, uiDevice).includeJankyRuns().build());
+    }
+
+    @Test
+    public void checkVisibility_navBarWindowIsAlwaysVisible() {
+        checkResults(result -> WmTraceSubject.assertThat(result)
+                .showsAboveAppWindow(NAVIGATION_BAR_WINDOW_TITLE).forAllEntries());
+    }
+
+    @Test
+    public void checkVisibility_statusBarWindowIsAlwaysVisible() {
+        checkResults(result -> WmTraceSubject.assertThat(result)
+                .showsAboveAppWindow(STATUS_BAR_WINDOW_TITLE).forAllEntries());
+    }
+
+    @Test
+    public void checkVisibility_dividerWindowBecomesVisible() {
+        checkResults(result -> WmTraceSubject.assertThat(result)
+                .hidesAboveAppWindow(DOCKED_STACK_DIVIDER)
+                .then()
+                .showsAboveAppWindow(DOCKED_STACK_DIVIDER)
+                .forAllEntries());
+    }
+
+    @Test
+    public void checkCoveredRegion_noUncoveredRegions() {
+        checkResults(result ->
+                LayersTraceSubject.assertThat(result)
+                        .coversRegion(getDisplayBounds()).forAllEntries());
+    }
+
+    @Test
+    public void checkVisibility_navBarLayerIsAlwaysVisible() {
+        checkResults(result -> LayersTraceSubject.assertThat(result)
+                .showsLayer(NAVIGATION_BAR_WINDOW_TITLE).forAllEntries());
+    }
+
+    @Test
+    public void checkVisibility_statusBarLayerIsAlwaysVisible() {
+        checkResults(result -> LayersTraceSubject.assertThat(result)
+                .showsLayer(STATUS_BAR_WINDOW_TITLE).forAllEntries());
+    }
+
+    @Test
+    public void checkVisibility_dividerLayerBecomesVisible() {
+        checkResults(result -> LayersTraceSubject.assertThat(result)
+                .hidesLayer(DOCKED_STACK_DIVIDER)
+                .then()
+                .showsLayer(DOCKED_STACK_DIVIDER)
+                .forAllEntries());
+    }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/OpenAppWarmTest.java b/tests/FlickerTests/src/com/android/server/wm/flicker/OpenAppWarmTest.java
new file mode 100644
index 0000000..de7639d
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/OpenAppWarmTest.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2018 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.server.wm.flicker;
+
+import static com.android.server.wm.flicker.CommonTransitions.openAppWarm;
+import static com.android.server.wm.flicker.WindowUtils.getDisplayBounds;
+import static com.android.server.wm.flicker.WmTraceSubject.assertThat;
+
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.LargeTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test warm launch app.
+ * To run this test: {@code atest FlickerTests:OpenAppWarmTest}
+ */
+@LargeTest
+@RunWith(AndroidJUnit4.class)
+public class OpenAppWarmTest extends FlickerTestBase {
+
+    public OpenAppWarmTest() {
+        this.testApp = new StandardAppHelper(InstrumentationRegistry.getInstrumentation(),
+                "com.android.server.wm.flicker.testapp", "SimpleApp");
+    }
+
+    @Before
+    public void runTransition() {
+        super.runTransition(openAppWarm(testApp, uiDevice).build());
+    }
+
+    @Test
+    public void checkVisibility_navBarIsAlwaysVisible() {
+        checkResults(result -> assertThat(result)
+                .showsAboveAppWindow(NAVIGATION_BAR_WINDOW_TITLE).forAllEntries());
+    }
+
+    @Test
+    public void checkVisibility_statusBarIsAlwaysVisible() {
+        checkResults(result -> assertThat(result)
+                .showsAboveAppWindow(STATUS_BAR_WINDOW_TITLE).forAllEntries());
+    }
+
+    @Test
+    public void checkVisibility_wallpaperBecomesInvisible() {
+        checkResults(result -> assertThat(result)
+                .showsBelowAppWindow("wallpaper")
+                .then()
+                .hidesBelowAppWindow("wallpaper")
+                .forAllEntries());
+    }
+
+    @Test
+    public void checkZOrder_appWindowReplacesLauncherAsTopWindow() {
+        checkResults(result -> assertThat(result)
+                .showsAppWindowOnTop(
+                        "com.google.android.apps.nexuslauncher/.NexusLauncherActivity")
+                .then()
+                .showsAppWindowOnTop(testApp.getPackage())
+                .forAllEntries());
+    }
+
+    @Test
+    public void checkCoveredRegion_noUncoveredRegions() {
+        checkResults(result -> LayersTraceSubject.assertThat(result).coversRegion(
+                getDisplayBounds()).forAllEntries());
+    }
+
+    @Test
+    public void checkVisibility_navBarLayerIsAlwaysVisible() {
+        checkResults(result -> LayersTraceSubject.assertThat(result)
+                .showsLayer(NAVIGATION_BAR_WINDOW_TITLE).forAllEntries());
+    }
+
+    @Test
+    public void checkVisibility_statusBarLayerIsAlwaysVisible() {
+        checkResults(result -> LayersTraceSubject.assertThat(result)
+                .showsLayer(STATUS_BAR_WINDOW_TITLE).forAllEntries());
+    }
+
+    @Test
+    public void checkVisibility_wallpaperLayerBecomesInvisible() {
+        checkResults(result -> LayersTraceSubject.assertThat(result)
+                .showsLayer("wallpaper")
+                .then()
+                .hidesLayer("wallpaper")
+                .forAllEntries());
+    }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/OpenImeWindowTest.java b/tests/FlickerTests/src/com/android/server/wm/flicker/OpenImeWindowTest.java
new file mode 100644
index 0000000..1bd519c
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/OpenImeWindowTest.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2018 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.server.wm.flicker;
+
+import static com.android.server.wm.flicker.CommonTransitions.editTextSetFocus;
+import static com.android.server.wm.flicker.WindowUtils.getDisplayBounds;
+
+import android.support.test.filters.LargeTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test IME window opening transitions.
+ * To run this test: {@code atest FlickerTests:OpenImeWindowTest}
+ */
+@LargeTest
+@RunWith(AndroidJUnit4.class)
+public class OpenImeWindowTest extends FlickerTestBase {
+
+    private static final String IME_WINDOW_TITLE = "InputMethod";
+
+    @Before
+    public void runTransition() {
+        super.runTransition(editTextSetFocus(uiDevice)
+                .includeJankyRuns().build());
+    }
+
+    @Test
+    public void checkVisibility_imeWindowBecomesVisible() {
+        checkResults(result -> WmTraceSubject.assertThat(result)
+                .hidesImeWindow(IME_WINDOW_TITLE)
+                .then()
+                .showsImeWindow(IME_WINDOW_TITLE)
+                .forAllEntries());
+    }
+
+    @Test
+    public void checkVisibility_imeLayerBecomesVisible() {
+        checkResults(result -> LayersTraceSubject.assertThat(result)
+                .hidesLayer(IME_WINDOW_TITLE)
+                .then()
+                .showsLayer(IME_WINDOW_TITLE)
+                .forAllEntries());
+    }
+
+    @Test
+    public void checkCoveredRegion_noUncoveredRegions() {
+        checkResults(result -> LayersTraceSubject.assertThat(result).coversRegion(
+                getDisplayBounds()).forAllEntries());
+    }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ResizeSplitScreenTest.java b/tests/FlickerTests/src/com/android/server/wm/flicker/ResizeSplitScreenTest.java
new file mode 100644
index 0000000..8a15cbd
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ResizeSplitScreenTest.java
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2018 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.server.wm.flicker;
+
+import static com.android.server.wm.flicker.CommonTransitions.resizeSplitScreen;
+import static com.android.server.wm.flicker.WindowUtils.getDisplayBounds;
+import static com.android.server.wm.flicker.WindowUtils.getDockedStackDividerInset;
+import static com.android.server.wm.flicker.WindowUtils.getNavigationBarHeight;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.graphics.Rect;
+import android.platform.helpers.IAppHelper;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.LargeTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.Rational;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test split screen resizing window transitions.
+ * To run this test: {@code atest FlickerTests:ResizeSplitScreenTest}
+ */
+@LargeTest
+@RunWith(AndroidJUnit4.class)
+public class ResizeSplitScreenTest extends FlickerTestBase {
+
+    public ResizeSplitScreenTest() {
+        this.testApp = new StandardAppHelper(InstrumentationRegistry.getInstrumentation(),
+                "com.android.server.wm.flicker.testapp", "SimpleApp");
+    }
+
+    @Before
+    public void runTransition() {
+        IAppHelper bottomApp = new StandardAppHelper(InstrumentationRegistry
+                .getInstrumentation(),
+                "com.android.server.wm.flicker.testapp", "ImeApp");
+        super.runTransition(resizeSplitScreen(testApp, bottomApp, uiDevice, new Rational(1, 3),
+                new Rational(2, 3)).includeJankyRuns().build());
+    }
+
+    @Test
+    public void checkVisibility_navBarLayerIsAlwaysVisible() {
+        checkResults(result -> LayersTraceSubject.assertThat(result)
+                .showsLayer(NAVIGATION_BAR_WINDOW_TITLE)
+                .forAllEntries());
+    }
+
+    @Test
+    public void checkVisibility_statusBarLayerIsAlwaysVisible() {
+        checkResults(result -> LayersTraceSubject.assertThat(result)
+                .showsLayer(STATUS_BAR_WINDOW_TITLE)
+                .forAllEntries());
+    }
+
+    @Test
+    public void checkVisibility_topAppLayerIsAlwaysVisible() {
+        checkResults(result -> LayersTraceSubject.assertThat(result)
+                .showsLayer("SimpleActivity")
+                .forAllEntries());
+    }
+
+    @Test
+    public void checkVisibility_bottomAppLayerIsAlwaysVisible() {
+        checkResults(result -> LayersTraceSubject.assertThat(result)
+                .showsLayer("ImeActivity")
+                .forAllEntries());
+    }
+
+    @Test
+    public void checkVisibility_dividerLayerIsAlwaysVisible() {
+        checkResults(result -> LayersTraceSubject.assertThat(result)
+                .showsLayer(DOCKED_STACK_DIVIDER)
+                .forAllEntries());
+    }
+
+    @Test
+    public void checkPosition_appsStartingBounds() {
+        Rect displayBounds = getDisplayBounds();
+        checkResults(result -> {
+            LayersTrace entries = LayersTrace.parseFrom(result.getLayersTrace(),
+                    result.getLayersTracePath());
+
+            assertThat(entries.getEntries()).isNotEmpty();
+            Rect startingDividerBounds = entries.getEntries().get(0).getVisibleBounds
+                    (DOCKED_STACK_DIVIDER);
+
+            Rect startingTopAppBounds = new Rect(0, 0, startingDividerBounds.right,
+                    startingDividerBounds.top + getDockedStackDividerInset());
+
+            Rect startingBottomAppBounds = new Rect(0,
+                    startingDividerBounds.bottom - getDockedStackDividerInset(),
+                    displayBounds.right,
+                    displayBounds.bottom - getNavigationBarHeight());
+
+            LayersTraceSubject.assertThat(result)
+                    .hasVisibleRegion("SimpleActivity", startingTopAppBounds)
+                    .inTheBeginning();
+
+            LayersTraceSubject.assertThat(result)
+                    .hasVisibleRegion("ImeActivity", startingBottomAppBounds)
+                    .inTheBeginning();
+        });
+    }
+
+    @Test
+    public void checkPosition_appsEndingBounds() {
+        Rect displayBounds = getDisplayBounds();
+        checkResults(result -> {
+            LayersTrace entries = LayersTrace.parseFrom(result.getLayersTrace(),
+                    result.getLayersTracePath());
+
+            assertThat(entries.getEntries()).isNotEmpty();
+            Rect endingDividerBounds = entries.getEntries().get(
+                    entries.getEntries().size() - 1).getVisibleBounds(
+                    DOCKED_STACK_DIVIDER);
+
+            Rect startingTopAppBounds = new Rect(0, 0, endingDividerBounds.right,
+                    endingDividerBounds.top + getDockedStackDividerInset());
+
+            Rect startingBottomAppBounds = new Rect(0,
+                    endingDividerBounds.bottom - getDockedStackDividerInset(),
+                    displayBounds.right,
+                    displayBounds.bottom - getNavigationBarHeight());
+
+            LayersTraceSubject.assertThat(result)
+                    .hasVisibleRegion("SimpleActivity", startingTopAppBounds)
+                    .atTheEnd();
+
+            LayersTraceSubject.assertThat(result)
+                    .hasVisibleRegion("ImeActivity", startingBottomAppBounds)
+                    .atTheEnd();
+        });
+    }
+
+    @Test
+    public void checkVisibility_navBarWindowIsAlwaysVisible() {
+        checkResults(result -> WmTraceSubject.assertThat(result)
+                .showsAboveAppWindow(NAVIGATION_BAR_WINDOW_TITLE)
+                .forAllEntries());
+    }
+
+    @Test
+    public void checkVisibility_statusBarWindowIsAlwaysVisible() {
+        checkResults(result -> WmTraceSubject.assertThat(result)
+                .showsAboveAppWindow(STATUS_BAR_WINDOW_TITLE)
+                .forAllEntries());
+    }
+
+    @Test
+    public void checkVisibility_topAppWindowIsAlwaysVisible() {
+        checkResults(result -> WmTraceSubject.assertThat(result)
+                .showsAppWindow("SimpleActivity")
+                .forAllEntries());
+    }
+
+    @Test
+    public void checkVisibility_bottomAppWindowIsAlwaysVisible() {
+        checkResults(result -> WmTraceSubject.assertThat(result)
+                .showsAppWindow("ImeActivity")
+                .forAllEntries());
+    }
+
+    @Test
+    public void checkVisibility_dividerWindowIsAlwaysVisible() {
+        checkResults(result -> WmTraceSubject.assertThat(result)
+                .showsAboveAppWindow(DOCKED_STACK_DIVIDER)
+                .forAllEntries());
+    }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/SeamlessAppRotationTest.java b/tests/FlickerTests/src/com/android/server/wm/flicker/SeamlessAppRotationTest.java
new file mode 100644
index 0000000..3eab68d
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/SeamlessAppRotationTest.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2018 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.server.wm.flicker;
+
+import static android.view.Surface.rotationToString;
+
+import static com.android.server.wm.flicker.CommonTransitions.changeAppRotation;
+import static com.android.server.wm.flicker.WindowUtils.getAppPosition;
+import static com.android.server.wm.flicker.WindowUtils.getDisplayBounds;
+import static com.android.server.wm.flicker.WindowUtils.getNavigationBarPosition;
+import static com.android.server.wm.flicker.testapp.ActivityOptions.EXTRA_STARVE_UI_THREAD;
+import static com.android.server.wm.flicker.testapp.ActivityOptions.SEAMLESS_ACTIVITY_COMPONENT_NAME;
+
+import android.content.Intent;
+import android.graphics.Rect;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.LargeTest;
+import android.view.Surface;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+/**
+ * Cycle through supported app rotations using seamless rotations.
+ * To run this test: {@code atest FlickerTests:SeamlessAppRotationTest}
+ */
+@LargeTest
+@RunWith(Parameterized.class)
+public class SeamlessAppRotationTest extends FlickerTestBase {
+    private int mBeginRotation;
+    private int mEndRotation;
+    private Intent mIntent;
+
+    public SeamlessAppRotationTest(String testId, Intent intent, int beginRotation,
+            int endRotation) {
+        this.mIntent = intent;
+        this.mBeginRotation = beginRotation;
+        this.mEndRotation = endRotation;
+    }
+
+    @Parameters(name = "{0}")
+    public static Collection<Object[]> getParams() {
+        int[] supportedRotations =
+                {Surface.ROTATION_0, Surface.ROTATION_90, Surface.ROTATION_270};
+        Collection<Object[]> params = new ArrayList<>();
+
+        ArrayList<Intent> testIntents = new ArrayList<>();
+
+        // launch test activity that supports seamless rotation
+        Intent intent = new Intent(Intent.ACTION_MAIN);
+        intent.setComponent(SEAMLESS_ACTIVITY_COMPONENT_NAME);
+        testIntents.add(intent);
+
+        // launch test activity that supports seamless rotation with a busy UI thread to miss frames
+        // when the app is asked to redraw
+        intent = new Intent(intent);
+        intent.putExtra(EXTRA_STARVE_UI_THREAD, true);
+        testIntents.add(intent);
+
+        for (Intent testIntent : testIntents) {
+            for (int begin : supportedRotations) {
+                for (int end : supportedRotations) {
+                    if (begin != end) {
+                        String testId = rotationToString(begin) + "_" + rotationToString(end);
+                        if (testIntent.getExtras() != null &&
+                                testIntent.getExtras().getBoolean(EXTRA_STARVE_UI_THREAD)) {
+                            testId += "_" + "BUSY_UI_THREAD";
+                        }
+                        params.add(new Object[]{testId, testIntent, begin, end});
+                    }
+                }
+            }
+        }
+        return params;
+    }
+
+    @Before
+    public void runTransition() {
+        String intentId = "";
+        if (mIntent.getExtras() != null &&
+                mIntent.getExtras().getBoolean(EXTRA_STARVE_UI_THREAD)) {
+            intentId = "BUSY_UI_THREAD";
+        }
+
+        super.runTransition(
+                changeAppRotation(mIntent, intentId, InstrumentationRegistry.getContext(),
+                        uiDevice, mBeginRotation, mEndRotation).repeat(5).build());
+    }
+
+    @Test
+    public void checkVisibility_navBarWindowIsAlwaysVisible() {
+        checkResults(result -> WmTraceSubject.assertThat(result)
+                .showsAboveAppWindow(NAVIGATION_BAR_WINDOW_TITLE).forAllEntries());
+    }
+
+    @Test
+    public void checkPosition_navBarLayerRotatesAndScales() {
+        Rect startingPos = getNavigationBarPosition(mBeginRotation);
+        Rect endingPos = getNavigationBarPosition(mEndRotation);
+        if (startingPos.equals(endingPos)) {
+            checkResults(result -> LayersTraceSubject.assertThat(result)
+                    .hasVisibleRegion(NAVIGATION_BAR_WINDOW_TITLE, startingPos)
+                    .forAllEntries());
+        } else {
+            checkResults(result -> LayersTraceSubject.assertThat(result)
+                    .hasVisibleRegion(NAVIGATION_BAR_WINDOW_TITLE, startingPos)
+                    .inTheBeginning());
+            checkResults(result -> LayersTraceSubject.assertThat(result)
+                    .hasVisibleRegion(NAVIGATION_BAR_WINDOW_TITLE, endingPos)
+                    .atTheEnd());
+        }
+    }
+
+    @Test
+    public void checkPosition_appLayerRotates() {
+        Rect startingPos = getAppPosition(mBeginRotation);
+        Rect endingPos = getAppPosition(mEndRotation);
+        if (startingPos.equals(endingPos)) {
+            checkResults(result -> LayersTraceSubject.assertThat(result)
+                    .hasVisibleRegion(mIntent.getComponent().getPackageName(), startingPos)
+                    .forAllEntries());
+        } else {
+            checkResults(result -> LayersTraceSubject.assertThat(result)
+                    .hasVisibleRegion(mIntent.getComponent().getPackageName(), startingPos)
+                    .then()
+                    .hasVisibleRegion(mIntent.getComponent().getPackageName(), endingPos)
+                    .forAllEntries());
+        }
+    }
+
+    @Test
+    public void checkCoveredRegion_noUncoveredRegions() {
+        Rect startingBounds = getDisplayBounds(mBeginRotation);
+        Rect endingBounds = getDisplayBounds(mEndRotation);
+        if (startingBounds.equals(endingBounds)) {
+            checkResults(result ->
+                    LayersTraceSubject.assertThat(result)
+                            .coversRegion(startingBounds)
+                            .forAllEntries());
+        } else {
+            checkResults(result ->
+                    LayersTraceSubject.assertThat(result)
+                            .coversRegion(startingBounds)
+                            .then()
+                            .coversRegion(endingBounds)
+                            .forAllEntries());
+        }
+    }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/SplitScreenToLauncherTest.java b/tests/FlickerTests/src/com/android/server/wm/flicker/SplitScreenToLauncherTest.java
new file mode 100644
index 0000000..40bd4e9
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/SplitScreenToLauncherTest.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2018 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.server.wm.flicker;
+
+import static com.android.server.wm.flicker.CommonTransitions.splitScreenToLauncher;
+import static com.android.server.wm.flicker.WindowUtils.getDisplayBounds;
+
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.FlakyTest;
+import android.support.test.filters.LargeTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test open app to split screen.
+ * To run this test: {@code atest FlickerTests:SplitScreenToLauncherTest}
+ */
+@LargeTest
+@RunWith(AndroidJUnit4.class)
+public class SplitScreenToLauncherTest extends FlickerTestBase {
+
+    public SplitScreenToLauncherTest() {
+        this.testApp = new StandardAppHelper(InstrumentationRegistry.getInstrumentation(),
+                "com.android.server.wm.flicker.testapp", "SimpleApp");
+    }
+
+    @Before
+    public void runTransition() {
+        super.runTransition(splitScreenToLauncher(testApp, uiDevice).includeJankyRuns().build());
+    }
+
+    @Test
+    public void checkCoveredRegion_noUncoveredRegions() {
+        checkResults(result ->
+                LayersTraceSubject.assertThat(result)
+                        .coversRegion(getDisplayBounds()).forAllEntries());
+    }
+
+    @Test
+    public void checkVisibility_dividerLayerBecomesInVisible() {
+        checkResults(result -> LayersTraceSubject.assertThat(result)
+                .showsLayer(DOCKED_STACK_DIVIDER)
+                .then()
+                .hidesLayer(DOCKED_STACK_DIVIDER)
+                .forAllEntries());
+    }
+
+    @FlakyTest(bugId = 79686616)
+    @Test
+    public void checkVisibility_appLayerBecomesInVisible() {
+        checkResults(result -> LayersTraceSubject.assertThat(result)
+                .showsLayer(testApp.getPackage())
+                .then()
+                .hidesLayer(testApp.getPackage())
+                .forAllEntries());
+    }
+
+    @Test
+    public void checkVisibility_navBarWindowIsAlwaysVisible() {
+        checkResults(result -> WmTraceSubject.assertThat(result)
+                .showsAboveAppWindow(NAVIGATION_BAR_WINDOW_TITLE).forAllEntries());
+    }
+
+    @Test
+    public void checkVisibility_statusBarWindowIsAlwaysVisible() {
+        checkResults(result -> WmTraceSubject.assertThat(result)
+                .showsAboveAppWindow(STATUS_BAR_WINDOW_TITLE).forAllEntries());
+    }
+
+    @Test
+    public void checkVisibility_dividerWindowBecomesInVisible() {
+        checkResults(result -> WmTraceSubject.assertThat(result)
+                .showsAboveAppWindow(DOCKED_STACK_DIVIDER)
+                .then()
+                .hidesAboveAppWindow(DOCKED_STACK_DIVIDER)
+                .forAllEntries());
+    }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/StandardAppHelper.java b/tests/FlickerTests/src/com/android/server/wm/flicker/StandardAppHelper.java
new file mode 100644
index 0000000..79a0220
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/StandardAppHelper.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2018 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.server.wm.flicker;
+
+import android.app.Instrumentation;
+import android.platform.helpers.AbstractStandardAppHelper;
+
+/**
+ * Class to take advantage of {@code IAppHelper} interface so the same test can be run against
+ * first party and third party apps.
+ */
+public class StandardAppHelper extends AbstractStandardAppHelper {
+    private final String mPackageName;
+    private final String mLauncherName;
+
+    public StandardAppHelper(Instrumentation instr, String packageName, String launcherName) {
+        super(instr);
+        mPackageName = packageName;
+        mLauncherName = launcherName;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public String getPackage() {
+        return mPackageName;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public String getLauncherName() {
+        return mLauncherName;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void dismissInitialDialogs() {
+
+    }
+}
diff --git a/tests/FlickerTests/test-apps/Android.mk b/tests/FlickerTests/test-apps/Android.mk
new file mode 100644
index 0000000..9af9f444
--- /dev/null
+++ b/tests/FlickerTests/test-apps/Android.mk
@@ -0,0 +1,15 @@
+# Copyright (C) 2018 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.
+
+include $(call all-subdir-makefiles)
diff --git a/tests/FlickerTests/test-apps/flickerapp/Android.mk b/tests/FlickerTests/test-apps/flickerapp/Android.mk
new file mode 100644
index 0000000..b916900
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/Android.mk
@@ -0,0 +1,31 @@
+# Copyright (C) 2018 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.
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+LOCAL_PACKAGE_NAME := FlickerTestApp
+LOCAL_MODULE_TAGS := tests optional
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+LOCAL_SDK_VERSION := current
+LOCAL_COMPATIBILITY_SUITE := device-tests
+include $(BUILD_PACKAGE)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := flickertestapplib
+LOCAL_MODULE_TAGS := tests optional
+LOCAL_SDK_VERSION := current
+LOCAL_SRC_FILES := src/com/android/server/wm/flicker/testapp/ActivityOptions.java
+include $(BUILD_STATIC_JAVA_LIBRARY)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
\ No newline at end of file
diff --git a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
new file mode 100644
index 0000000..b694172
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
@@ -0,0 +1,64 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 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.
+-->
+
+<manifest
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.server.wm.flicker.testapp">
+
+    <uses-sdk android:minSdkVersion="17" android:targetSdkVersion="27"/>
+    <application
+        android:allowBackup="false"
+        android:supportsRtl="true">
+        <activity android:name=".SimpleActivity"
+                  android:taskAffinity="com.android.server.wm.flicker.testapp.SimpleActivity"
+                  android:label="SimpleApp">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="android.intent.category.LAUNCHER"/>
+            </intent-filter>
+        </activity>
+        <activity android:name=".ImeActivity"
+                  android:taskAffinity="com.android.server.wm.flicker.testapp.ImeActivity"
+                  android:label="ImeApp">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="android.intent.category.LAUNCHER"/>
+            </intent-filter>
+        </activity>
+        <activity android:name=".PipActivity"
+                  android:resizeableActivity="true"
+                  android:supportsPictureInPicture="true"
+                  android:configChanges=
+                      "screenSize|smallestScreenSize|screenLayout|orientation"
+                  android:taskAffinity="com.android.server.wm.flicker.testapp.PipActivity"
+                  android:label="PipApp">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="android.intent.category.LAUNCHER"/>
+            </intent-filter>
+        </activity>
+        <activity android:name=".SeamlessRotationActivity"
+                  android:taskAffinity=
+                      "com.android.server.wm.flicker.testapp.SeamlessRotationActivity"
+                  android:configChanges="orientation|screenSize"
+                  android:label="SeamlessApp">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="android.intent.category.LAUNCHER"/>
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>
\ No newline at end of file
diff --git a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_ime.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_ime.xml
new file mode 100644
index 0000000..d5eb023
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_ime.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2018 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.
+-->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:background="@android:color/holo_green_light">
+    <EditText android:id="@+id/plain_text_input"
+              android:layout_height="wrap_content"
+              android:layout_width="match_parent"
+              android:inputType="text"/>
+</LinearLayout>
diff --git a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_pip.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_pip.xml
new file mode 100644
index 0000000..2c58d91
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_pip.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2018 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.
+-->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:background="@android:color/holo_blue_bright">
+    <Button android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:id="@+id/enter_pip"
+            android:text="Enter PIP"/>
+</LinearLayout>
diff --git a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_simple.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_simple.xml
new file mode 100644
index 0000000..5d94e51
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_simple.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2018 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.
+-->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:background="@android:color/holo_orange_light">
+
+</LinearLayout>
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java
new file mode 100644
index 0000000..1899411
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2018 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.server.wm.flicker.testapp;
+
+import android.content.ComponentName;
+
+public class ActivityOptions {
+    public static final String EXTRA_STARVE_UI_THREAD = "StarveUiThread";
+    public static final ComponentName SEAMLESS_ACTIVITY_COMPONENT_NAME =
+            new ComponentName("com.android.server.wm.flicker.testapp",
+                    "com.android.server.wm.flicker.testapp.SeamlessRotationActivity");
+}
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ImeActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ImeActivity.java
new file mode 100644
index 0000000..df60460
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ImeActivity.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2018 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.server.wm.flicker.testapp;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.WindowManager;
+
+public class ImeActivity extends Activity {
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        WindowManager.LayoutParams p = getWindow().getAttributes();
+        p.layoutInDisplayCutoutMode = WindowManager.LayoutParams
+                .LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
+        getWindow().setAttributes(p);
+        setContentView(R.layout.activity_ime);
+    }
+}
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/PipActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/PipActivity.java
new file mode 100644
index 0000000..9a8f399
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/PipActivity.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2018 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.server.wm.flicker.testapp;
+
+import android.app.Activity;
+import android.app.PictureInPictureParams;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.util.Rational;
+import android.view.WindowManager;
+import android.widget.Button;
+
+public class PipActivity extends Activity {
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        WindowManager.LayoutParams p = getWindow().getAttributes();
+        p.layoutInDisplayCutoutMode = WindowManager.LayoutParams
+                .LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
+        getWindow().setAttributes(p);
+        setContentView(R.layout.activity_pip);
+        Button enterPip = (Button) findViewById(R.id.enter_pip);
+
+        PictureInPictureParams params = new PictureInPictureParams.Builder()
+                .setAspectRatio(new Rational(1, 1))
+                .setSourceRectHint(new Rect(0, 0, 100, 100))
+                .build();
+
+        enterPip.setOnClickListener((v) -> enterPictureInPictureMode(params));
+    }
+}
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/SeamlessRotationActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/SeamlessRotationActivity.java
new file mode 100644
index 0000000..3a0c1c9
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/SeamlessRotationActivity.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2018 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.server.wm.flicker.testapp;
+
+import static android.os.SystemClock.sleep;
+import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
+
+import static com.android.server.wm.flicker.testapp.ActivityOptions.EXTRA_STARVE_UI_THREAD;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.view.Window;
+import android.view.WindowManager;
+
+import java.util.Timer;
+import java.util.TimerTask;
+
+public class SeamlessRotationActivity extends Activity {
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        enableSeamlessRotation();
+        setContentView(R.layout.activity_simple);
+        boolean starveUiThread = getIntent().getExtras() != null &&
+                getIntent().getExtras().getBoolean(EXTRA_STARVE_UI_THREAD);
+        if (starveUiThread) {
+            starveUiThread();
+        }
+    }
+
+    private void starveUiThread() {
+        Handler handler = new Handler(Looper.getMainLooper(), (Message unused) -> {
+            sleep(20);
+            return true;
+        });
+        new Timer().schedule(new TimerTask() {
+            @Override
+            public void run() {
+                handler.sendEmptyMessage(0);
+            }
+        }, 0, 21);
+    }
+
+    private void enableSeamlessRotation() {
+        WindowManager.LayoutParams p = getWindow().getAttributes();
+        p.rotationAnimation = WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS;
+        p.layoutInDisplayCutoutMode = WindowManager.LayoutParams
+                .LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
+        requestWindowFeature(Window.FEATURE_NO_TITLE);
+        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
+                WindowManager.LayoutParams.FLAG_FULLSCREEN);
+        getWindow().setAttributes(p);
+    }
+}
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/SimpleActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/SimpleActivity.java
new file mode 100644
index 0000000..699abf8
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/SimpleActivity.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2018 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.server.wm.flicker.testapp;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.WindowManager;
+
+public class SimpleActivity extends Activity {
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        WindowManager.LayoutParams p = getWindow().getAttributes();
+        p.layoutInDisplayCutoutMode = WindowManager.LayoutParams
+                .LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
+        getWindow().setAttributes(p);
+        setContentView(R.layout.activity_simple);
+    }
+}
diff --git a/vr/java/com/google/vr/platform/DeviceInfo.java b/vr/java/com/google/vr/platform/DeviceInfo.java
index f6da66b..6a4617d 100644
--- a/vr/java/com/google/vr/platform/DeviceInfo.java
+++ b/vr/java/com/google/vr/platform/DeviceInfo.java
@@ -1,5 +1,6 @@
 package com.google.vr.platform;
 
+import android.annotation.UnsupportedAppUsage;
 import android.os.SystemProperties;
 
 /**
@@ -13,6 +14,7 @@
     /**
      * Returns true if this device boots directly in VR mode.
      */
+    @UnsupportedAppUsage
     public static boolean getVrBoot() {
         return SystemProperties.getBoolean(VR_MODE_BOOT, false);
     }
diff --git a/vr/java/com/google/vr/platform/Dvr.java b/vr/java/com/google/vr/platform/Dvr.java
index b07d634..41dcd87 100644
--- a/vr/java/com/google/vr/platform/Dvr.java
+++ b/vr/java/com/google/vr/platform/Dvr.java
@@ -1,5 +1,7 @@
 package com.google.vr.platform;
 
+import android.annotation.UnsupportedAppUsage;
+
 /**
  * Class to load the dvr api.
  * @hide
@@ -10,6 +12,7 @@
      *
      * @return A Long object describing the handle returned by dlopen.
      */
+    @UnsupportedAppUsage
     public static Long loadLibrary() {
         // Load a thin JNI library that runs dlopen on request.
         System.loadLibrary("dvr_loader");