Merge "Clean Paint.mBidiFlags as it is no longer used"
diff --git a/tests/accessibility/AndroidManifest.xml b/tests/accessibility/AndroidManifest.xml
index dde1de8..0d18cef 100644
--- a/tests/accessibility/AndroidManifest.xml
+++ b/tests/accessibility/AndroidManifest.xml
@@ -19,6 +19,8 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
           package="android.view.accessibility.services">
 
+  <uses-permission android:name="android.permission.CAN_REQUEST_TOUCH_EXPLORATION_MODE"/>
+
   <application>
 
     <service android:name=".SpeakingAccessibilityService"
diff --git a/tests/accessibility/res/xml/speaking_accessibilityservice.xml b/tests/accessibility/res/xml/speaking_accessibilityservice.xml
index 630940b..d43d3e7 100644
--- a/tests/accessibility/res/xml/speaking_accessibilityservice.xml
+++ b/tests/accessibility/res/xml/speaking_accessibilityservice.xml
@@ -16,5 +16,5 @@
 <accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
     android:accessibilityEventTypes="typeAllMask"
     android:accessibilityFeedbackType="feedbackSpoken"
-    android:accessibilityFlags="flagDefault"
+    android:accessibilityFlags="flagDefault|flagRequestTouchExplorationMode"
     android:canRetrieveWindowContent="true" />
diff --git a/tests/accessibility/res/xml/vibrating_accessibilityservice.xml b/tests/accessibility/res/xml/vibrating_accessibilityservice.xml
index 4ccec76..c2f8799 100644
--- a/tests/accessibility/res/xml/vibrating_accessibilityservice.xml
+++ b/tests/accessibility/res/xml/vibrating_accessibilityservice.xml
@@ -14,7 +14,7 @@
      limitations under the License.
 -->
 <accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
-    android:accessibilityEventTypes="typeAllMask"
+    android:accessibilityEventTypes="typeAllMask|"
     android:accessibilityFeedbackType="feedbackHaptic"
-    android:accessibilityFlags="flagDefault"
+    android:accessibilityFlags="flagDefault|flagRequestTouchExplorationMode"
     android:canRetrieveWindowContent="true" />
diff --git a/tests/accessibilityservice/AndroidManifest.xml b/tests/accessibilityservice/AndroidManifest.xml
index 805f697..117578e 100644
--- a/tests/accessibilityservice/AndroidManifest.xml
+++ b/tests/accessibilityservice/AndroidManifest.xml
@@ -19,6 +19,8 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
           package="android.accessibilityservice.delegate">
 
+  <uses-permission android:name="android.permission.CAN_REQUEST_TOUCH_EXPLORATION_MODE"/>
+
   <application>
 
     <uses-library android:name="android.test.runner"/>
diff --git a/tests/accessibilityservice/res/xml/accessibilityservice.xml b/tests/accessibilityservice/res/xml/accessibilityservice.xml
index 395d0224..69e0651 100644
--- a/tests/accessibilityservice/res/xml/accessibilityservice.xml
+++ b/tests/accessibilityservice/res/xml/accessibilityservice.xml
@@ -16,7 +16,7 @@
 <accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
     android:accessibilityEventTypes="typeAllMask"
     android:accessibilityFeedbackType="feedbackGeneric"
-    android:accessibilityFlags="flagDefault"
+    android:accessibilityFlags="flagDefault|flagRequestTouchExplorationMode"
     android:canRetrieveWindowContent="true"
     android:notificationTimeout="50"
     android:settingsActivity="android.accessibilityservice.delegate.SomeActivity"
diff --git a/tests/expectations/knownfailures.txt b/tests/expectations/knownfailures.txt
index 1992b11..d7ef39f 100644
--- a/tests/expectations/knownfailures.txt
+++ b/tests/expectations/knownfailures.txt
@@ -2,5 +2,9 @@
 {
   name: "android.openglperf.cts.GlVboPerfTest#testVboWithVaryingIndexBufferNumbers",
   bug: 6950385
+},
+{
+  name: "android.holo.cts.HoloTest",
+  bug: 8148617
 }
 ]
diff --git a/tests/tests/accessibility/src/android/view/accessibility/cts/AccessibilityManagerTest.java b/tests/tests/accessibility/src/android/view/accessibility/cts/AccessibilityManagerTest.java
index 28e26ebf..7ac0572 100644
--- a/tests/tests/accessibility/src/android/view/accessibility/cts/AccessibilityManagerTest.java
+++ b/tests/tests/accessibility/src/android/view/accessibility/cts/AccessibilityManagerTest.java
@@ -52,10 +52,6 @@
         assertEquals("Accessibility should have been enabled by the test runner.",
                 1, Settings.Secure.getInt(mContext.getContentResolver(),
                         Settings.Secure.ACCESSIBILITY_ENABLED));
-
-        assertEquals("Touch exploration should have been enabled by the test runner.",
-                1, Settings.Secure.getInt(mContext.getContentResolver(),
-                        Settings.Secure.TOUCH_EXPLORATION_ENABLED));
     }
 
     public void testAddAndRemoveAccessibilityStateChangeListener() throws Exception {
diff --git a/tests/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityServiceInfoTest.java b/tests/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityServiceInfoTest.java
index 0f687d0..1d28470 100644
--- a/tests/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityServiceInfoTest.java
+++ b/tests/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityServiceInfoTest.java
@@ -110,7 +110,9 @@
         AccessibilityServiceInfo speakingService = enabledServices.get(0);
         assertSame(AccessibilityEvent.TYPES_ALL_MASK, speakingService.eventTypes);
         assertSame(AccessibilityServiceInfo.FEEDBACK_GENERIC, speakingService.feedbackType);
-        assertSame(AccessibilityServiceInfo.DEFAULT, speakingService.flags);
+        assertSame(AccessibilityServiceInfo.DEFAULT
+                | AccessibilityServiceInfo.FLAG_REQUEST_TOUCH_EXPLORATION_MODE,
+                speakingService.flags);
         assertSame(50l, speakingService.notificationTimeout);
         assertEquals("Delegating Accessibility Service", speakingService.getDescription());
         assertNull(speakingService.packageNames /*all packages*/);
diff --git a/tests/tests/text/src/android/text/bidi/cts/BidiFormatterTest.java b/tests/tests/text/src/android/text/bidi/cts/BidiFormatterTest.java
new file mode 100644
index 0000000..198a52f
--- /dev/null
+++ b/tests/tests/text/src/android/text/bidi/cts/BidiFormatterTest.java
@@ -0,0 +1,422 @@
+/*
+ * Copyright (C) 2013 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.text.bidi.cts;
+
+import android.test.AndroidTestCase;
+import android.text.TextDirectionHeuristics;
+import android.text.bidi.BidiFormatter;
+
+import java.util.Locale;
+
+public class BidiFormatterTest extends AndroidTestCase {
+
+    private static final BidiFormatter LTR_FMT = BidiFormatter.getInstance(false /* LTR context */);
+    private static final BidiFormatter RTL_FMT = BidiFormatter.getInstance(true /* RTL context */);
+
+    private static final BidiFormatter LTR_FMT_EXIT_RESET =
+            new BidiFormatter.Builder(false /* LTR context */).stereoReset(false).build();
+    private static final BidiFormatter RTL_FMT_EXIT_RESET =
+            new BidiFormatter.Builder(true /* RTL context */).stereoReset(false).build();
+
+    private static final String EN = "abba";
+    private static final String HE = "\u05e0\u05e1";
+
+    private static final String LRM = "\u200E";
+    private static final String RLM = "\u200F";
+    private static final String LRE = "\u202A";
+    private static final String RLE = "\u202B";
+    private static final String PDF = "\u202C";
+
+    private static final String LEFT = "left";
+    private static final String RIGHT = "right";
+
+
+    public void testIsRtlContext() {
+        assertEquals(false, LTR_FMT.isRtlContext());
+        assertEquals(true, RTL_FMT.isRtlContext());
+
+        assertEquals(false, BidiFormatter.getInstance(Locale.ENGLISH).isRtlContext());
+        assertEquals(true, BidiFormatter.getInstance(true).isRtlContext());
+    }
+
+    public void testBuilderIsRtlContext() {
+        assertEquals(false, new BidiFormatter.Builder(false).build().isRtlContext());
+        assertEquals(true, new BidiFormatter.Builder(true).build().isRtlContext());
+    }
+
+    public void testIsRtl() {
+        assertEquals(true, BidiFormatter.getInstance(true).isRtl(HE));
+        assertEquals(true, BidiFormatter.getInstance(false).isRtl(HE));
+
+        assertEquals(false, BidiFormatter.getInstance(true).isRtl(EN));
+        assertEquals(false, BidiFormatter.getInstance(false).isRtl(EN));
+    }
+
+    public void testDirAttrValue() {
+        assertEquals("ltr", LTR_FMT.dirAttrValue(EN));
+        assertEquals("ltr", RTL_FMT.dirAttrValue(EN));
+
+        assertEquals("rtl", LTR_FMT.dirAttrValue(HE));
+        assertEquals("rtl", RTL_FMT.dirAttrValue(HE));
+
+        assertEquals("ltr", LTR_FMT.dirAttrValue(EN, TextDirectionHeuristics.LTR));
+        assertEquals("rtl", LTR_FMT.dirAttrValue(EN, TextDirectionHeuristics.RTL));
+
+        assertEquals("ltr", RTL_FMT.dirAttrValue(EN, TextDirectionHeuristics.LTR));
+        assertEquals("rtl", RTL_FMT.dirAttrValue(EN, TextDirectionHeuristics.RTL));
+
+        assertEquals("ltr", LTR_FMT.dirAttrValue(HE, TextDirectionHeuristics.LTR));
+        assertEquals("rtl", LTR_FMT.dirAttrValue(HE, TextDirectionHeuristics.RTL));
+
+        assertEquals("ltr", RTL_FMT.dirAttrValue(HE, TextDirectionHeuristics.LTR));
+        assertEquals("rtl", RTL_FMT.dirAttrValue(HE, TextDirectionHeuristics.RTL));
+
+        assertEquals("ltr", LTR_FMT.dirAttrValue("", TextDirectionHeuristics.LTR));
+        assertEquals("rtl", LTR_FMT.dirAttrValue("", TextDirectionHeuristics.RTL));
+
+        assertEquals("ltr", RTL_FMT.dirAttrValue("", TextDirectionHeuristics.LTR));
+        assertEquals("rtl", RTL_FMT.dirAttrValue("", TextDirectionHeuristics.RTL));
+    }
+
+    public void testDirAttr() {
+        assertEquals("", LTR_FMT.dirAttr(EN));
+        assertEquals("dir=\"ltr\"", RTL_FMT.dirAttr(EN));
+
+        assertEquals("dir=\"rtl\"", LTR_FMT.dirAttr(HE));
+        assertEquals("", RTL_FMT.dirAttr(HE));
+
+        assertEquals("", LTR_FMT.dirAttr(".", TextDirectionHeuristics.LTR));
+        assertEquals("dir=\"ltr\"", RTL_FMT.dirAttr(".", TextDirectionHeuristics.LTR));
+
+        assertEquals("dir=\"rtl\"", LTR_FMT.dirAttr(".", TextDirectionHeuristics.RTL));
+        assertEquals("", RTL_FMT.dirAttr(".", TextDirectionHeuristics.RTL));
+    }
+
+    public void testMarkAfter() {
+        assertEquals("uniform dir matches LTR context",
+                "", LTR_FMT.markAfter(EN));
+        assertEquals("uniform dir matches RTL context",
+                "", RTL_FMT.markAfter(HE));
+
+        assertEquals("exit dir opposite to LTR context",
+                LRM, LTR_FMT.markAfter(EN + HE, TextDirectionHeuristics.LTR));
+        assertEquals("exit dir opposite to RTL context",
+                RLM, RTL_FMT.markAfter(HE + EN, TextDirectionHeuristics.RTL));
+
+        assertEquals("overall dir (but not exit dir) opposite to LTR context",
+                LRM, LTR_FMT.markAfter(HE + EN, TextDirectionHeuristics.RTL));
+        assertEquals("overall dir (but not exit dir) opposite to RTL context",
+                RLM, RTL_FMT.markAfter(EN + HE, TextDirectionHeuristics.LTR));
+
+        assertEquals("exit dir neutral, overall dir matches LTR context",
+                "", LTR_FMT.markAfter(".", TextDirectionHeuristics.LTR));
+        assertEquals("exit dir neutral, overall dir matches RTL context",
+                "", RTL_FMT.markAfter(".", TextDirectionHeuristics.RTL));
+    }
+
+    public void testMarkBefore() {
+        assertEquals("uniform dir matches LTR context",
+                "", LTR_FMT.markBefore(EN));
+        assertEquals("uniform dir matches RTL context",
+                "", RTL_FMT.markBefore(HE));
+
+        assertEquals("entry dir opposite to LTR context",
+                LRM, LTR_FMT.markBefore(HE + EN, TextDirectionHeuristics.LTR));
+        assertEquals("entry dir opposite to RTL context",
+                RLM, RTL_FMT.markBefore(EN + HE, TextDirectionHeuristics.RTL));
+
+        assertEquals("overall dir (but not entry dir) opposite to LTR context",
+                LRM, LTR_FMT.markBefore(EN + HE, TextDirectionHeuristics.RTL));
+        assertEquals("overall dir (but not entry dir) opposite to RTL context",
+                RLM, RTL_FMT.markBefore(HE + EN, TextDirectionHeuristics.LTR));
+
+        assertEquals("exit dir neutral, overall dir matches LTR context",
+                "", LTR_FMT.markBefore(".", TextDirectionHeuristics.LTR));
+        assertEquals("exit dir neutral, overall dir matches RTL context",
+                "", RTL_FMT.markBefore(".", TextDirectionHeuristics.RTL));
+    }
+
+
+    public void testMark() {
+        assertEquals(LRM, LTR_FMT.mark());
+        assertEquals(RLM, RTL_FMT.mark());
+    }
+
+    public void testStartEdge() {
+        assertEquals(LEFT, LTR_FMT.startEdge());
+        assertEquals(RIGHT, RTL_FMT.startEdge());
+    }
+
+    public void testEndEdge() {
+        assertEquals(RIGHT, LTR_FMT.endEdge());
+        assertEquals(LEFT, RTL_FMT.endEdge());
+    }
+
+    public void testUnicodeWrap() {
+        // Uniform directionality in opposite context.
+        assertEquals("uniform dir opposite to LTR context",
+                RLE + "." + HE + "." + PDF + LRM,
+                LTR_FMT_EXIT_RESET.unicodeWrap("." + HE + "."));
+        assertEquals("uniform dir opposite to LTR context, stereo reset",
+                LRM + RLE + "." + HE + "." + PDF + LRM,
+                LTR_FMT.unicodeWrap("." + HE + "."));
+        assertEquals("uniform dir opposite to LTR context, stereo reset, no isolation",
+                RLE + "." + HE + "." + PDF,
+                LTR_FMT.unicodeWrap("." + HE + ".", false));
+        assertEquals("neutral treated as opposite to LTR context",
+                RLE + "." + PDF + LRM,
+                LTR_FMT_EXIT_RESET.unicodeWrap(".", TextDirectionHeuristics.RTL));
+        assertEquals("uniform dir opposite to RTL context",
+                LRE + "." + EN + "." + PDF + RLM,
+                RTL_FMT_EXIT_RESET.unicodeWrap("." + EN + "."));
+        assertEquals("uniform dir opposite to RTL context, stereo reset",
+                RLM + LRE + "." + EN + "." + PDF + RLM,
+                RTL_FMT.unicodeWrap("." + EN + "."));
+        assertEquals("uniform dir opposite to RTL context, stereo reset, no isolation",
+                LRE + "." + EN + "." + PDF,
+                RTL_FMT.unicodeWrap("." + EN + ".", false));
+        assertEquals("neutral treated as opposite to RTL context",
+                LRE + "." + PDF + RLM,
+                RTL_FMT_EXIT_RESET.unicodeWrap(".", TextDirectionHeuristics.LTR));
+
+        // We test mixed-directionality cases only with an explicit overall directionality parameter
+        // because the estimation logic is outside the sphere of BidiFormatter, and different
+        // estimators will treat them differently.
+
+        // Overall directionality matching context, but with opposite exit directionality.
+        assertEquals("exit dir opposite to LTR context",
+                EN + HE + LRM,
+                LTR_FMT_EXIT_RESET.unicodeWrap(EN + HE, TextDirectionHeuristics.LTR));
+        assertEquals("exit dir opposite to LTR context, stereo reset",
+                EN + HE + LRM,
+                LTR_FMT.unicodeWrap(EN + HE, TextDirectionHeuristics.LTR));
+        assertEquals("exit dir opposite to LTR context, stereo reset, no isolation",
+                EN + HE,
+                LTR_FMT.unicodeWrap(EN + HE, TextDirectionHeuristics.LTR, false));
+
+        assertEquals("exit dir opposite to RTL context",
+                HE + EN + RLM,
+                RTL_FMT_EXIT_RESET.unicodeWrap(HE + EN, TextDirectionHeuristics.RTL));
+        assertEquals("exit dir opposite to RTL context, stereo reset",
+                HE + EN + RLM,
+                RTL_FMT.unicodeWrap(HE + EN, TextDirectionHeuristics.RTL));
+        assertEquals("exit dir opposite to RTL context, stereo reset, no isolation",
+                HE + EN,
+                RTL_FMT.unicodeWrap(HE + EN, TextDirectionHeuristics.RTL, false));
+
+        // Overall directionality matching context, but with opposite entry directionality.
+        assertEquals("entry dir opposite to LTR context",
+                HE + EN,
+                LTR_FMT_EXIT_RESET.unicodeWrap(HE + EN, TextDirectionHeuristics.LTR));
+        assertEquals("entry dir opposite to LTR context, stereo reset",
+                LRM + HE + EN,
+                LTR_FMT.unicodeWrap(HE + EN, TextDirectionHeuristics.LTR));
+        assertEquals("entry dir opposite to LTR context, stereo reset, no isolation",
+                HE + EN,
+                LTR_FMT.unicodeWrap(HE + EN, TextDirectionHeuristics.LTR, false));
+
+        assertEquals("entry dir opposite to RTL context",
+                EN + HE,
+                RTL_FMT_EXIT_RESET.unicodeWrap(EN + HE, TextDirectionHeuristics.RTL));
+        assertEquals("entry dir opposite to RTL context, stereo reset",
+                RLM + EN + HE,
+                RTL_FMT.unicodeWrap(EN + HE, TextDirectionHeuristics.RTL));
+        assertEquals("entry dir opposite to RTL context, stereo reset, no isolation",
+                EN + HE,
+                RTL_FMT.unicodeWrap(EN + HE, TextDirectionHeuristics.RTL, false));
+
+        // Overall directionality matching context, but with opposite entry and exit directionality.
+        assertEquals("entry and exit dir opposite to LTR context",
+                HE + EN + HE + LRM,
+                LTR_FMT_EXIT_RESET.unicodeWrap(HE + EN + HE, TextDirectionHeuristics.LTR));
+        assertEquals("entry and exit dir opposite to LTR context, stereo reset",
+                LRM + HE + EN + HE + LRM,
+                LTR_FMT.unicodeWrap(HE + EN + HE, TextDirectionHeuristics.LTR));
+        assertEquals("entry and exit dir opposite to LTR context, no isolation",
+                HE + EN + HE,
+                LTR_FMT_EXIT_RESET.unicodeWrap(HE + EN + HE, TextDirectionHeuristics.LTR, false));
+
+        assertEquals("entry and exit dir opposite to RTL context",
+                EN + HE + EN + RLM,
+                RTL_FMT_EXIT_RESET.unicodeWrap(EN + HE + EN, TextDirectionHeuristics.RTL));
+        assertEquals("entry and exit dir opposite to RTL context, no isolation",
+                EN + HE + EN,
+                RTL_FMT_EXIT_RESET.unicodeWrap(EN + HE + EN, TextDirectionHeuristics.RTL, false));
+
+        // Entry and exit directionality matching context, but with opposite overall directionality.
+        assertEquals("overall dir (but not entry or exit dir) opposite to LTR context",
+                RLE + EN + HE + EN + PDF + LRM,
+                LTR_FMT_EXIT_RESET.unicodeWrap(EN + HE + EN, TextDirectionHeuristics.RTL));
+        assertEquals("overall dir (but not entry or exit dir) opposite to LTR context, stereo reset",
+                LRM + RLE + EN + HE + EN + PDF + LRM,
+                LTR_FMT.unicodeWrap(EN + HE + EN, TextDirectionHeuristics.RTL));
+        assertEquals("overall dir (but not entry or exit dir) opposite to LTR context, no isolation",
+                RLE + EN + HE + EN + PDF,
+                LTR_FMT_EXIT_RESET.unicodeWrap(EN + HE + EN, TextDirectionHeuristics.RTL, false));
+
+        assertEquals("overall dir (but not entry or exit dir) opposite to RTL context",
+                LRE + HE + EN + HE + PDF + RLM,
+                RTL_FMT_EXIT_RESET.unicodeWrap(HE + EN + HE, TextDirectionHeuristics.LTR));
+        assertEquals("overall dir (but not entry or exit dir) opposite to RTL context, stereo reset",
+                RLM + LRE + HE + EN + HE + PDF + RLM,
+                RTL_FMT.unicodeWrap(HE + EN + HE, TextDirectionHeuristics.LTR));
+        assertEquals("overall dir (but not entry or exit dir) opposite to RTL context, no isolation",
+                LRE + HE + EN + HE + PDF,
+                RTL_FMT_EXIT_RESET.unicodeWrap(HE + EN + HE, TextDirectionHeuristics.LTR, false));
+    }
+
+
+    public void testSpanWrap() {
+        // Uniform directionality in matching context.
+        assertEquals("uniform dir matches LTR context",
+                "&amp; " + EN + "&lt;", LTR_FMT.spanWrap("& " + EN + "<"));
+        assertEquals("neutral treated as matching LTR context",
+                ".", LTR_FMT.spanWrap(".", TextDirectionHeuristics.LTR));
+        assertEquals("uniform dir matches RTL context",
+                "&amp; " + HE + "&lt;", RTL_FMT.spanWrap("& " + HE + "<"));
+        assertEquals("neutral treated as matching RTL context",
+                ".", RTL_FMT.spanWrap(".", TextDirectionHeuristics.RTL));
+
+        // Uniform directionality in opposite context.
+        assertEquals("uniform dir opposite to LTR context",
+                "<span dir=\"rtl\">." + HE + ".</span>" + LRM,
+                LTR_FMT_EXIT_RESET.spanWrap("." + HE + "."));
+        assertEquals("uniform dir opposite to LTR context, stereo reset",
+                LRM + "<span dir=\"rtl\">." + HE + ".</span>" + LRM,
+                LTR_FMT.spanWrap("." + HE + "."));
+        assertEquals("uniform dir opposite to LTR context, no isolation",
+                "<span dir=\"rtl\">." + HE + ".</span>",
+                LTR_FMT_EXIT_RESET.spanWrap("." + HE + ".", false));
+        assertEquals("uniform dir opposite to LTR context, stereo reset, no isolation",
+                "<span dir=\"rtl\">." + HE + ".</span>",
+                LTR_FMT.spanWrap("." + HE + ".", false));
+        assertEquals("neutral treated as opposite to LTR context",
+                "<span dir=\"rtl\">" + "." + "</span>" + LRM,
+                LTR_FMT_EXIT_RESET.spanWrap(".", TextDirectionHeuristics.RTL));
+        assertEquals("uniform dir opposite to RTL context",
+                "<span dir=\"ltr\">." + EN + ".</span>" + RLM,
+                RTL_FMT_EXIT_RESET.spanWrap("." + EN + "."));
+        assertEquals("uniform dir opposite to RTL context, stereo reset",
+                RLM + "<span dir=\"ltr\">." + EN + ".</span>" + RLM,
+                RTL_FMT.spanWrap("." + EN + "."));
+        assertEquals("uniform dir opposite to RTL context, no isolation",
+                "<span dir=\"ltr\">." + EN + ".</span>",
+                RTL_FMT_EXIT_RESET.spanWrap("." + EN + ".", false));
+        assertEquals("uniform dir opposite to RTL context, stereo reset, no isolation",
+                "<span dir=\"ltr\">." + EN + ".</span>",
+                RTL_FMT.spanWrap("." + EN + ".", false));
+        assertEquals("neutral treated as opposite to RTL context",
+                "<span dir=\"ltr\">" + "." + "</span>" + RLM,
+                RTL_FMT_EXIT_RESET.spanWrap(".", TextDirectionHeuristics.LTR));
+
+        // We test mixed-directionality cases only with an explicit overall directionality parameter
+        // because the estimation logic is outside the sphere of BidiFormatter, and different
+        // estimators will treat them differently.
+
+        // Overall directionality matching context, but with opposite exit directionality.
+        assertEquals("exit dir opposite to LTR context",
+                EN + HE + LRM,
+                LTR_FMT_EXIT_RESET.spanWrap(EN + HE, TextDirectionHeuristics.LTR));
+        assertEquals("exit dir opposite to LTR context, stereo reset",
+                EN + HE + LRM,
+                LTR_FMT.spanWrap(EN + HE, TextDirectionHeuristics.LTR));
+        assertEquals("exit dir opposite to LTR context, no isolation",
+                EN + HE,
+                LTR_FMT_EXIT_RESET.spanWrap(EN + HE, TextDirectionHeuristics.LTR, false));
+        assertEquals("exit dir opposite to LTR context, stereo reset, no isolation",
+                EN + HE,
+                LTR_FMT.spanWrap(EN + HE, TextDirectionHeuristics.LTR, false));
+        assertEquals("exit dir opposite to RTL context",
+                HE + EN + RLM,
+                RTL_FMT_EXIT_RESET.spanWrap(HE + EN, TextDirectionHeuristics.RTL));
+        assertEquals("exit dir opposite to RTL context, stereo reset",
+                HE + EN + RLM,
+                RTL_FMT.spanWrap(HE + EN, TextDirectionHeuristics.RTL));
+        assertEquals("exit dir opposite to RTL context, no isolation",
+                HE + EN,
+                RTL_FMT_EXIT_RESET.spanWrap(HE + EN, TextDirectionHeuristics.RTL, false));
+        assertEquals("exit dir opposite to RTL context, stereo reset, no isolation",
+                HE + EN,
+                RTL_FMT.spanWrap( HE + EN, TextDirectionHeuristics.RTL, false));
+
+        // Overall directionality matching context, but with opposite entry directionality.
+        assertEquals("entry dir opposite to LTR context",
+                HE + EN,
+                LTR_FMT_EXIT_RESET.spanWrap(HE + EN, TextDirectionHeuristics.LTR));
+        assertEquals("entry dir opposite to LTR context, stereo reset",
+                LRM + HE + EN,
+                LTR_FMT.spanWrap(HE + EN, TextDirectionHeuristics.LTR));
+        assertEquals("entry dir opposite to LTR context, no isolation",
+                HE + EN,
+                LTR_FMT_EXIT_RESET.spanWrap(HE + EN, TextDirectionHeuristics.LTR, false));
+        assertEquals("entry dir opposite to LTR context, stereo reset, no isolation",
+                HE + EN,
+                LTR_FMT.spanWrap(HE + EN, TextDirectionHeuristics.LTR, false));
+        assertEquals("entry dir opposite to RTL context",
+                EN + HE,
+                RTL_FMT_EXIT_RESET.spanWrap(EN + HE, TextDirectionHeuristics.RTL));
+        assertEquals("entry dir opposite to RTL context, stereo reset",
+                RLM + EN + HE,
+                RTL_FMT.spanWrap(EN + HE, TextDirectionHeuristics.RTL));
+        assertEquals("entry dir opposite to RTL context, no isolation",
+                EN + HE,
+                RTL_FMT_EXIT_RESET.spanWrap(EN + HE, TextDirectionHeuristics.RTL, false));
+        assertEquals("entry dir opposite to RTL context, stereo reset, no isolation",
+                EN + HE,
+                RTL_FMT.spanWrap(EN + HE, TextDirectionHeuristics.RTL, false));
+
+        // Overall directionality matching context, but with opposite entry and exit directionality.
+        assertEquals("entry and exit dir opposite to LTR context",
+                HE + EN + HE + LRM,
+                LTR_FMT_EXIT_RESET.spanWrap(HE + EN + HE, TextDirectionHeuristics.LTR));
+        assertEquals("entry and exit dir opposite to LTR context, stereo reset",
+                LRM + HE + EN + HE + LRM,
+                LTR_FMT.spanWrap(HE + EN + HE, TextDirectionHeuristics.LTR));
+        assertEquals("entry and exit dir opposite to LTR context, no isolation",
+                HE + EN + HE,
+                LTR_FMT_EXIT_RESET.spanWrap(HE + EN + HE, TextDirectionHeuristics.LTR, false));
+        assertEquals("entry and exit dir opposite to RTL context",
+                EN + HE + EN + RLM,
+                RTL_FMT_EXIT_RESET.spanWrap(EN + HE + EN, TextDirectionHeuristics.RTL));
+        assertEquals("entry and exit dir opposite to RTL context, stereo reset",
+                RLM + EN + HE + EN + RLM,
+                RTL_FMT.spanWrap(EN + HE + EN, TextDirectionHeuristics.RTL));
+        assertEquals("entry and exit dir opposite to RTL context, no isolation",
+                EN + HE + EN,
+                RTL_FMT_EXIT_RESET.spanWrap(EN + HE + EN, TextDirectionHeuristics.RTL, false));
+
+        // Entry and exit directionality matching context, but with opposite overall directionality.
+        assertEquals("overall dir (but not entry or exit dir) opposite to LTR context",
+                "<span dir=\"rtl\">" + EN + HE + EN + "</span>" + LRM,
+                LTR_FMT_EXIT_RESET.spanWrap(EN + HE + EN, TextDirectionHeuristics.RTL));
+        assertEquals("overall dir (but not entry or exit dir) opposite to LTR context, stereo reset",
+                LRM + "<span dir=\"rtl\">" + EN + HE + EN + "</span>" + LRM,
+                LTR_FMT.spanWrap(EN + HE + EN, TextDirectionHeuristics.RTL));
+        assertEquals("overall dir (but not entry or exit dir) opposite to LTR context, no isolation",
+                "<span dir=\"rtl\">" + EN + HE + EN + "</span>",
+                LTR_FMT_EXIT_RESET.spanWrap(EN + HE + EN, TextDirectionHeuristics.RTL, false));
+        assertEquals("overall dir (but not entry or exit dir) opposite to RTL context",
+                "<span dir=\"ltr\">" + HE + EN + HE + "</span>" + RLM,
+                RTL_FMT_EXIT_RESET.spanWrap(HE + EN + HE, TextDirectionHeuristics.LTR));
+        assertEquals("overall dir (but not entry or exit dir) opposite to RTL context, stereo reset",
+                RLM + "<span dir=\"ltr\">" + HE + EN + HE + "</span>" + RLM,
+                RTL_FMT.spanWrap(HE + EN + HE, TextDirectionHeuristics.LTR));
+        assertEquals("overall dir (but not entry or exit dir) opposite to RTL context, no isolation",
+                "<span dir=\"ltr\">" + HE + EN + HE + "</span>",
+                RTL_FMT_EXIT_RESET.spanWrap(HE + EN + HE, TextDirectionHeuristics.LTR, false));
+    }
+}
diff --git a/tools/tradefed-host/.classpath b/tools/tradefed-host/.classpath
index 6be8c99..0b1866d 100644
--- a/tools/tradefed-host/.classpath
+++ b/tools/tradefed-host/.classpath
@@ -5,8 +5,7 @@
 	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
 	<classpathentry exported="true" kind="var" path="CTS_SRC_ROOT/out/host/common/obj/JAVA_LIBRARIES/ctsdeviceinfolib_intermediates/javalib.jar"/>
 	<classpathentry exported="true" kind="var" path="CTS_SRC_ROOT/out/host/common/obj/JAVA_LIBRARIES/hosttestlib_intermediates/javalib.jar"/>
-	<classpathentry kind="var" path="CTS_SRC_ROOT/out/host/common/obj/JAVA_LIBRARIES/junit_intermediates/javalib.jar"/>
-	<classpathentry kind="var" path="CTS_SRC_ROOT/out/host/common/obj/JAVA_LIBRARIES/tradefed-prebuilt_intermediates/tradefed-prebuilt.jar"/>
-	<classpathentry kind="var" path="CTS_SRC_ROOT/out/host/common/obj/JAVA_LIBRARIES/ddmlib-prebuilt_intermediates/ddmlib-prebuilt.jar"/>
+	<classpathentry kind="var" path="CTS_SRC_ROOT/out/host/common/obj/JAVA_LIBRARIES/ddmlib-prebuilt_intermediates/ddmlib-prebuilt.jar" sourcepath="/SDK_SRC_ROOT"/>
+	<classpathentry kind="var" path="CTS_SRC_ROOT/out/host/common/obj/JAVA_LIBRARIES/tradefed-prebuilt_intermediates/tradefed-prebuilt.jar" sourcepath="/TRADEFED_ROOT/tools/tradefederation/src"/>
 	<classpathentry kind="output" path="bin"/>
 </classpath>
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/AccessibilityServiceTestRunner.java b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/AccessibilityServiceTestRunner.java
index 7490f18..d333943 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/AccessibilityServiceTestRunner.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/AccessibilityServiceTestRunner.java
@@ -67,7 +67,7 @@
     }
 
     private void afterTest() throws DeviceNotAvailableException {
-        AccessibilityTestRunner.disableAccessibilityAndServicesAndTouchExploration(getDevice());
+        AccessibilityTestRunner.disableAccessibilityAndServices(getDevice());
         uninstallAndAssert(DELEGATING_ACCESSIBLITY_SERVICE_PACKAGE_NAME);
     }
 
@@ -85,7 +85,7 @@
     private void enableAccessibilityAndDelegatingService() throws DeviceNotAvailableException {
         String componentName = DELEGATING_ACCESSIBLITY_SERVICE_PACKAGE_NAME + "/"
             + DELEGATING_ACCESSIBLITY_SERVICE_NAME;
-        AccessibilityTestRunner.enableAccessibilityAndServicesAndTouchExploration(getDevice(),
+        AccessibilityTestRunner.enableAccessibilityAndServices(getDevice(),
                 componentName);
     }
 }
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/AccessibilityTestRunner.java b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/AccessibilityTestRunner.java
index ca4f9e1..2a4239a 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/AccessibilityTestRunner.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/AccessibilityTestRunner.java
@@ -68,11 +68,11 @@
 
     private void beforeTest() throws DeviceNotAvailableException {
         installApkAndAssert(SOME_ACCESSIBLITY_SERVICES_APK);
-        enableAccessibilityAndServicesAndTouchExploration();
+        enableAccessibilityAndServices();
     }
 
     private void afterTest() throws DeviceNotAvailableException {
-        disableAccessibilityAndServicesAndTouchExploration(getDevice());
+        disableAccessibilityAndServices(getDevice());
         uninstallAndAssert(SOME_ACCESSIBLITY_SERVICES_PACKAGE_NAME);
     }
 
@@ -87,26 +87,27 @@
         TestCase.assertNull("Error uninstalling: " + packageName, errorMessage);
     }
 
-    private void enableAccessibilityAndServicesAndTouchExploration()
-            throws DeviceNotAvailableException {
+    private void enableAccessibilityAndServices() throws DeviceNotAvailableException {
         String enabledServicesValue =
               SOME_ACCESSIBLITY_SERVICES_PACKAGE_NAME + "/" + SPEAKING_ACCESSIBLITY_SERVICE_NAME
             + ":"
             + SOME_ACCESSIBLITY_SERVICES_PACKAGE_NAME + "/" + VIBRATING_ACCESSIBLITY_SERVICE_NAME;
-        enableAccessibilityAndServicesAndTouchExploration(getDevice(), enabledServicesValue);
+        enableAccessibilityAndServices(getDevice(), enabledServicesValue);
     }
 
-    static void enableAccessibilityAndServicesAndTouchExploration(ITestDevice device, String value)
+    static void enableAccessibilityAndServices(ITestDevice device, String value)
             throws DeviceNotAvailableException {
         SettingsToggler.setSecureString(device, "enabled_accessibility_services", value);
+        SettingsToggler.setSecureString(device,
+                "touch_exploration_granted_accessibility_services", value);
         SettingsToggler.setSecureInt(device, "accessibility_enabled", 1);
-        SettingsToggler.setSecureInt(device, "touch_exploration_enabled", 1);
     }
 
-    static void disableAccessibilityAndServicesAndTouchExploration(ITestDevice device)
+    static void disableAccessibilityAndServices(ITestDevice device)
             throws DeviceNotAvailableException {
         SettingsToggler.updateSecureString(device, "enabled_accessibility_services", "");
+        SettingsToggler.updateSecureString(device,
+                "touch_exploration_granted_accessibility_services", "");
         SettingsToggler.updateSecureInt(device, "accessibility_enabled", 0);
-        SettingsToggler.updateSecureInt(device, "touch_exploration_enabled", 0);
     }
 }
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/CtsTest.java b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/CtsTest.java
index 19ee3b7..b79f8a5 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/CtsTest.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/CtsTest.java
@@ -149,6 +149,9 @@
             "Interval between each reboot in min.")
     private int mRebootIntervalMin = 30;
 
+    @Option(name = "screenshot-on-ui-failure", description =
+            "take a screenshot if a test fails with a message indicating a UI obstruction.")
+    private boolean mScreenshotOnUiFailures = false;
 
     private long mPrevRebootTime; // last reboot time
 
@@ -200,12 +203,45 @@
         public void testFailed(TestFailure status, TestIdentifier test, String trace) {
             super.testFailed(status, test, trace);
             InputStreamSource bugSource = mDevice.getBugreport();
-            super.testLog(String.format("bug-%s", test.toString()), LogDataType.TEXT,
-                    bugSource);
+            super.testLog(String.format("bug-%s_%s", test.getClassName(), test.getTestName()),
+                    LogDataType.TEXT, bugSource);
             bugSource.cancel();
         }
     }
 
+    /**
+     * A {@link ResultForwarder} that will forward a screenshot when it detects a failed test due
+     * to a UI obstruction.
+     */
+    private static class FailedUiTestScreenshotGenerator extends ResultForwarder {
+        private ITestDevice mDevice;
+
+        public FailedUiTestScreenshotGenerator(ITestInvocationListener listener,
+                ITestDevice device) {
+            super(listener);
+            mDevice = device;
+        }
+
+        @Override
+        public void testFailed(TestFailure status, TestIdentifier test, String trace) {
+            super.testFailed(status, test, trace);
+
+            if (trace.contains(
+                    "Injecting to another application requires INJECT_EVENTS permission")) {
+                try {
+                    InputStreamSource screenSource = mDevice.getScreenshot();
+                    super.testLog(String.format("screenshot-%s_%s", test.getClassName(),
+                            test.getTestName()), LogDataType.PNG, screenSource);
+                    screenSource.cancel();
+                } catch (DeviceNotAvailableException e) {
+                    // TODO: rethrow this somehow
+                    CLog.e("Device %s became unavailable while capturing screenshot, %s",
+                            mDevice.getSerialNumber(), e.toString());
+                }
+            }
+        }
+    }
+
     /** list of remaining tests to execute */
     private List<TestPackage> mRemainingTestPkgs = null;
 
@@ -337,6 +373,11 @@
                     getDevice());
             listener = bugListener;
         }
+        if (mScreenshotOnUiFailures) {
+            FailedUiTestScreenshotGenerator screenListener = new FailedUiTestScreenshotGenerator(
+                    listener, getDevice());
+            listener = screenListener;
+        }
 
         // collect and install the prerequisiteApks first, to save time when multiple test
         // packages are using the same prerequisite apk (I'm looking at you, CtsTestStubs!)