Merge "Fix drawable CTS breakages" into mnc-dev
diff --git a/api/current.txt b/api/current.txt
index 376037a..99c5fa0 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -3997,24 +3997,22 @@
   public class AssistContent implements android.os.Parcelable {
     ctor public AssistContent();
     method public int describeContents();
-    method public static android.app.AssistContent getAssistContent(android.os.Bundle);
     method public android.content.ClipData getClipData();
     method public android.content.Intent getIntent();
+    method public android.net.Uri getWebUri();
     method public void setClipData(android.content.ClipData);
     method public void setIntent(android.content.Intent);
+    method public void setWebUri(android.net.Uri);
     method public void writeToParcel(android.os.Parcel, int);
-    field public static final java.lang.String ASSIST_KEY = "android:assist_content";
     field public static final android.os.Parcelable.Creator<android.app.AssistContent> CREATOR;
   }
 
   public final class AssistStructure implements android.os.Parcelable {
     method public int describeContents();
     method public android.content.ComponentName getActivityComponent();
-    method public static android.app.AssistStructure getAssistStructure(android.os.Bundle);
     method public android.app.AssistStructure.WindowNode getWindowNodeAt(int);
     method public int getWindowNodeCount();
     method public void writeToParcel(android.os.Parcel, int);
-    field public static final java.lang.String ASSIST_KEY = "android:assist_structure";
     field public static final android.os.Parcelable.Creator<android.app.AssistStructure> CREATOR;
   }
 
@@ -28916,7 +28914,7 @@
     method public android.view.View onCreateContentView();
     method public void onDestroy();
     method public boolean[] onGetSupportedCommands(android.service.voice.VoiceInteractionSession.Caller, java.lang.String[]);
-    method public void onHandleAssist(android.os.Bundle);
+    method public void onHandleAssist(android.os.Bundle, android.app.AssistStructure, android.app.AssistContent);
     method public void onHide();
     method public boolean onKeyDown(int, android.view.KeyEvent);
     method public boolean onKeyLongPress(int, android.view.KeyEvent);
diff --git a/api/system-current.txt b/api/system-current.txt
index e3c7414..588dff8 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -4092,24 +4092,22 @@
   public class AssistContent implements android.os.Parcelable {
     ctor public AssistContent();
     method public int describeContents();
-    method public static android.app.AssistContent getAssistContent(android.os.Bundle);
     method public android.content.ClipData getClipData();
     method public android.content.Intent getIntent();
+    method public android.net.Uri getWebUri();
     method public void setClipData(android.content.ClipData);
     method public void setIntent(android.content.Intent);
+    method public void setWebUri(android.net.Uri);
     method public void writeToParcel(android.os.Parcel, int);
-    field public static final java.lang.String ASSIST_KEY = "android:assist_content";
     field public static final android.os.Parcelable.Creator<android.app.AssistContent> CREATOR;
   }
 
   public final class AssistStructure implements android.os.Parcelable {
     method public int describeContents();
     method public android.content.ComponentName getActivityComponent();
-    method public static android.app.AssistStructure getAssistStructure(android.os.Bundle);
     method public android.app.AssistStructure.WindowNode getWindowNodeAt(int);
     method public int getWindowNodeCount();
     method public void writeToParcel(android.os.Parcel, int);
-    field public static final java.lang.String ASSIST_KEY = "android:assist_structure";
     field public static final android.os.Parcelable.Creator<android.app.AssistStructure> CREATOR;
   }
 
@@ -31042,7 +31040,7 @@
     method public android.view.View onCreateContentView();
     method public void onDestroy();
     method public boolean[] onGetSupportedCommands(android.service.voice.VoiceInteractionSession.Caller, java.lang.String[]);
-    method public void onHandleAssist(android.os.Bundle);
+    method public void onHandleAssist(android.os.Bundle, android.app.AssistStructure, android.app.AssistContent);
     method public void onHide();
     method public boolean onKeyDown(int, android.view.KeyEvent);
     method public boolean onKeyLongPress(int, android.view.KeyEvent);
diff --git a/cmds/app_process/Android.mk b/cmds/app_process/Android.mk
index ce6d7b5..3599695 100644
--- a/cmds/app_process/Android.mk
+++ b/cmds/app_process/Android.mk
@@ -52,11 +52,13 @@
 LOCAL_CPPFLAGS := -std=c++11
 
 LOCAL_MODULE := app_process__asan
-LOCAL_MODULE_TAGS := eng
-LOCAL_MODULE_PATH := $(TARGET_OUT_EXECUTABLES)/asan
-LOCAL_MODULE_STEM := app_process
+LOCAL_MULTILIB := both
+LOCAL_MODULE_STEM_32 := app_process32
+LOCAL_MODULE_STEM_64 := app_process64
+
 LOCAL_ADDRESS_SANITIZER := true
 LOCAL_CLANG := true
+LOCAL_MODULE_PATH := $(TARGET_OUT_EXECUTABLES)/asan
 
 LOCAL_CFLAGS += -Wall -Werror -Wunused -Wunreachable-code
 
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 02e0d5b..e4def1e 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -2193,7 +2193,9 @@
             data.enforceInterface(IActivityManager.descriptor);
             IBinder token = data.readStrongBinder();
             Bundle extras = data.readBundle();
-            reportAssistContextExtras(token, extras);
+            AssistStructure structure = AssistStructure.CREATOR.createFromParcel(data);
+            AssistContent content = AssistContent.CREATOR.createFromParcel(data);
+            reportAssistContextExtras(token, extras, structure, content);
             reply.writeNoException();
             return true;
         }
@@ -5359,13 +5361,15 @@
         reply.recycle();
     }
 
-    public void reportAssistContextExtras(IBinder token, Bundle extras)
-            throws RemoteException {
+    public void reportAssistContextExtras(IBinder token, Bundle extras, AssistStructure structure,
+            AssistContent content) throws RemoteException {
         Parcel data = Parcel.obtain();
         Parcel reply = Parcel.obtain();
         data.writeInterfaceToken(IActivityManager.descriptor);
         data.writeStrongBinder(token);
         data.writeBundle(extras);
+        structure.writeToParcel(data, 0);
+        content.writeToParcel(data, 0);
         mRemote.transact(REPORT_ASSIST_CONTEXT_EXTRAS_TRANSACTION, data, reply, 0);
         reply.readException();
         data.recycle();
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index cb436b5..2a98b6c 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -2562,15 +2562,18 @@
 
     public void handleRequestAssistContextExtras(RequestAssistContextExtras cmd) {
         Bundle data = new Bundle();
+        AssistStructure structure = null;
+        AssistContent content = new AssistContent();
         ActivityClientRecord r = mActivities.get(cmd.activityToken);
         if (r != null) {
             r.activity.getApplication().dispatchOnProvideAssistData(r.activity, data);
             r.activity.onProvideAssistData(data);
             if (cmd.requestType == ActivityManager.ASSIST_CONTEXT_FULL) {
-                data.putParcelable(AssistStructure.ASSIST_KEY, new AssistStructure(r.activity));
-                AssistContent content = new AssistContent();
+                structure = new AssistStructure(r.activity);
                 Intent activityIntent = r.activity.getIntent();
-                if (activityIntent != null) {
+                if (activityIntent != null && (r.window == null ||
+                        (r.window.getAttributes().flags
+                                & WindowManager.LayoutParams.FLAG_SECURE) == 0)) {
                     Intent intent = new Intent(activityIntent);
                     intent.setFlags(intent.getFlags() & ~(Intent.FLAG_GRANT_WRITE_URI_PERMISSION
                             | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION));
@@ -2580,15 +2583,14 @@
                     content.setIntent(new Intent());
                 }
                 r.activity.onProvideAssistContent(content);
-                data.putParcelable(AssistContent.ASSIST_KEY, content);
             }
         }
-        if (data.isEmpty()) {
-            data = null;
+        if (structure == null) {
+            structure = new AssistStructure();
         }
         IActivityManager mgr = ActivityManagerNative.getDefault();
         try {
-            mgr.reportAssistContextExtras(cmd.requestToken, data);
+            mgr.reportAssistContextExtras(cmd.requestToken, data, structure, content);
         } catch (RemoteException e) {
         }
     }
diff --git a/core/java/android/app/AssistContent.java b/core/java/android/app/AssistContent.java
index cb1a3f5..f271af1 100644
--- a/core/java/android/app/AssistContent.java
+++ b/core/java/android/app/AssistContent.java
@@ -18,6 +18,7 @@
 
 import android.content.ClipData;
 import android.content.Intent;
+import android.net.Uri;
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -30,14 +31,17 @@
 public class AssistContent implements Parcelable {
     private Intent mIntent;
     private ClipData mClipData;
+    private Uri mUri;
 
     /**
+     * @hide
      * Key name this data structure is stored in the Bundle generated by
      * {@link Activity#onProvideAssistData}.
      */
     public static final String ASSIST_KEY = "android:assist_content";
 
     /**
+     * @hide
      * Retrieve the framework-generated AssistContent that is stored within
      * the Bundle filled in by {@link Activity#onProvideAssistContent}.
      */
@@ -56,6 +60,13 @@
      */
     public void setIntent(Intent intent) {
         mIntent = intent;
+        setWebUri(null);
+        if (intent != null && Intent.ACTION_VIEW.equals(intent.getAction())) {
+            Uri uri = intent.getData();
+            if ("http".equals(uri.getScheme()) || "https".equals(uri.getScheme())) {
+                setWebUri(uri);
+            }
+        }
     }
 
     /**
@@ -81,6 +92,30 @@
         return mClipData;
     }
 
+    /**
+     * Set a web URI associated with the current data being shown to the user.
+     * This URI could be opened in a web browser, or in the app as an
+     * {@link Intent#ACTION_VIEW} Intent, to show the same data that is currently
+     * being displayed by it.  The URI here should be something that is transportable
+     * off the device into other environments to acesss the same data as is currently
+     * being shown in the app; if the app does not have such a representation, it should
+     * leave the null and only report the local intent and clip data.
+     *
+     * <p>This will be automatically populated for you from {@link #setIntent} if that Intent
+     * is an {@link Intent#ACTION_VIEW} of a web (http or https scheme) URI.</p>
+     */
+    public void setWebUri(Uri uri) {
+        mUri = uri;
+    }
+
+    /**
+     * Return the content's web URI as per {@link #setWebUri(android.net.Uri)}, or null if
+     * there is none.
+     */
+    public Uri getWebUri() {
+        return mUri;
+    }
+
     AssistContent(Parcel in) {
         if (in.readInt() != 0) {
             mIntent = Intent.CREATOR.createFromParcel(in);
@@ -88,6 +123,9 @@
         if (in.readInt() != 0) {
             mClipData = ClipData.CREATOR.createFromParcel(in);
         }
+        if (in.readInt() != 0) {
+            mUri = Uri.CREATOR.createFromParcel(in);
+        }
     }
 
     @Override
@@ -109,6 +147,12 @@
         } else {
             dest.writeInt(0);
         }
+        if (mUri != null) {
+            dest.writeInt(1);
+            mUri.writeToParcel(dest, flags);
+        } else {
+            dest.writeInt(0);
+        }
     }
 
     public static final Parcelable.Creator<AssistContent> CREATOR
diff --git a/core/java/android/app/AssistStructure.java b/core/java/android/app/AssistStructure.java
index b703b0e..bb9cb3b 100644
--- a/core/java/android/app/AssistStructure.java
+++ b/core/java/android/app/AssistStructure.java
@@ -42,13 +42,13 @@
 
 /**
  * Assist data automatically created by the platform's implementation
- * of {@link Activity#onProvideAssistData}.  Retrieve it from the assist
- * data with {@link #getAssistStructure(android.os.Bundle)}.
+ * of {@link Activity#onProvideAssistData}.
  */
 final public class AssistStructure implements Parcelable {
     static final String TAG = "AssistStructure";
 
     /**
+     * @hide
      * Key name this data structure is stored in the Bundle generated by
      * {@link Activity#onProvideAssistData}.
      */
@@ -741,6 +741,11 @@
         }
     }
 
+    AssistStructure() {
+        mHaveData = true;
+        mActivityComponent = null;
+    }
+
     AssistStructure(Parcel in) {
         mReceiveChannel = in.readStrongBinder();
     }
@@ -811,6 +816,7 @@
     }
 
     /**
+     * @hide
      * Retrieve the framework-generated AssistStructure that is stored within
      * the Bundle filled in by {@link Activity#onProvideAssistData}.
      */
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index c42719b..0a425ae 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -433,7 +433,8 @@
     public void requestAssistContextExtras(int requestType, IResultReceiver receiver)
             throws RemoteException;
 
-    public void reportAssistContextExtras(IBinder token, Bundle extras) throws RemoteException;
+    public void reportAssistContextExtras(IBinder token, Bundle extras,
+            AssistStructure structure, AssistContent content) throws RemoteException;
 
     public boolean launchAssistIntent(Intent intent, int requestType, String hint, int userHandle)
             throws RemoteException;
diff --git a/core/java/android/os/RecoverySystem.java b/core/java/android/os/RecoverySystem.java
index 4aeab49..0c79094 100644
--- a/core/java/android/os/RecoverySystem.java
+++ b/core/java/android/os/RecoverySystem.java
@@ -71,6 +71,7 @@
     /** Used to communicate with recovery.  See bootable/recovery/recovery.c. */
     private static File RECOVERY_DIR = new File("/cache/recovery");
     private static File COMMAND_FILE = new File(RECOVERY_DIR, "command");
+    private static File UNCRYPT_FILE = new File(RECOVERY_DIR, "uncrypt_file");
     private static File LOG_FILE = new File(RECOVERY_DIR, "log");
     private static String LAST_PREFIX = "last_";
 
@@ -333,8 +334,21 @@
     public static void installPackage(Context context, File packageFile)
         throws IOException {
         String filename = packageFile.getCanonicalPath();
+
+        FileWriter uncryptFile = new FileWriter(UNCRYPT_FILE);
+        try {
+            uncryptFile.write(filename + "\n");
+        } finally {
+            uncryptFile.close();
+        }
         Log.w(TAG, "!!! REBOOTING TO INSTALL " + filename + " !!!");
 
+        // If the package is on the /data partition, write the block map file
+        // into COMMAND_FILE instead.
+        if (filename.startsWith("/data/")) {
+            filename = "@/cache/recovery/block.map";
+        }
+
         final String filenameArg = "--update_package=" + filename;
         final String localeArg = "--locale=" + Locale.getDefault().toString();
         bootCommand(context, filenameArg, localeArg);
diff --git a/core/java/android/service/voice/IVoiceInteractionSession.aidl b/core/java/android/service/voice/IVoiceInteractionSession.aidl
index 7c90261..894edac 100644
--- a/core/java/android/service/voice/IVoiceInteractionSession.aidl
+++ b/core/java/android/service/voice/IVoiceInteractionSession.aidl
@@ -16,6 +16,8 @@
 
 package android.service.voice;
 
+import android.app.AssistContent;
+import android.app.AssistStructure;
 import android.content.Intent;
 import android.graphics.Bitmap;
 import android.os.Bundle;
@@ -28,7 +30,7 @@
 oneway interface IVoiceInteractionSession {
     void show(in Bundle sessionArgs, int flags, IVoiceInteractionSessionShowCallback showCallback);
     void hide();
-    void handleAssist(in Bundle assistData);
+    void handleAssist(in Bundle assistData, in AssistStructure structure, in AssistContent content);
     void handleScreenshot(in Bitmap screenshot);
     void taskStarted(in Intent intent, int taskId);
     void taskFinished(in Intent intent, int taskId);
diff --git a/core/java/android/service/voice/VoiceInteractionSession.java b/core/java/android/service/voice/VoiceInteractionSession.java
index f122d10..f09b6a2 100644
--- a/core/java/android/service/voice/VoiceInteractionSession.java
+++ b/core/java/android/service/voice/VoiceInteractionSession.java
@@ -16,6 +16,7 @@
 
 package android.service.voice;
 
+import android.app.AssistContent;
 import android.app.AssistStructure;
 import android.app.Dialog;
 import android.app.Instrumentation;
@@ -180,21 +181,16 @@
         }
 
         @Override
-        public void handleAssist(Bundle assistBundle) {
+        public void handleAssist(Bundle data, AssistStructure structure,
+                AssistContent content) {
             // We want to pre-warm the AssistStructure before handing it off to the main
             // thread.  There is a strong argument to be made that it should be handed
             // through as a separate param rather than part of the assistBundle.
-            if (assistBundle != null) {
-                Bundle assistContext = assistBundle.getBundle(Intent.EXTRA_ASSIST_CONTEXT);
-                if (assistContext != null) {
-                    AssistStructure as = AssistStructure.getAssistStructure(assistContext);
-                    if (as != null) {
-                        as.ensureData();
-                    }
-                }
+            if (structure != null) {
+                structure.ensureData();
             }
-            mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageO(MSG_HANDLE_ASSIST,
-                    assistBundle));
+            mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageOOO(MSG_HANDLE_ASSIST,
+                    data, structure, content));
         }
 
         @Override
@@ -422,8 +418,11 @@
                     doDestroy();
                     break;
                 case MSG_HANDLE_ASSIST:
-                    if (DEBUG) Log.d(TAG, "onHandleAssist: " + msg.obj);
-                    onHandleAssist((Bundle) msg.obj);
+                    args = (SomeArgs)msg.obj;
+                    if (DEBUG) Log.d(TAG, "onHandleAssist: data=" + args.arg1
+                            + " structure=" + args.arg2 + " content=" + args.arg3);
+                    onHandleAssist((Bundle) args.arg1, (AssistStructure) args.arg2,
+                            (AssistContent) args.arg3);
                     break;
                 case MSG_HANDLE_SCREENSHOT:
                     if (DEBUG) Log.d(TAG, "onHandleScreenshot: " + msg.obj);
@@ -817,9 +816,22 @@
 
     }
 
+    /** @hide */
     public void onHandleAssist(Bundle assistBundle) {
     }
 
+    public void onHandleAssist(Bundle data, AssistStructure structure, AssistContent content) {
+        if (data != null) {
+            Bundle assistContext = data.getBundle(Intent.EXTRA_ASSIST_CONTEXT);
+            if (assistContext != null) {
+                assistContext.putParcelable(AssistStructure.ASSIST_KEY, structure);
+                assistContext.putParcelable(AssistContent.ASSIST_KEY, content);
+                data.putBundle(Intent.EXTRA_ASSIST_CONTEXT, assistContext);
+            }
+        }
+        onHandleAssist(data);
+    }
+
     /** @hide */
     public void onHandleScreenshot(Bitmap screenshot) {
     }
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 1a76264..d9801ef 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -413,6 +413,10 @@
     <!-- Spoken description for ringer normal option. [CHAR LIMIT=NONE] -->
     <string name="silent_mode_ring">Ringer on</string>
 
+    <!-- Reboot to Recovery Progress Dialog. This is shown before it reboots to recovery. -->
+    <string name="reboot_to_recovery_title">Prepare for update</string>
+    <string name="reboot_to_recovery_progress">Processing the update package\u2026</string>
+
     <!-- Shutdown Progress Dialog. This is shown if the user chooses to power off the phone. -->
     <string name="shutdown_progress">Shutting down\u2026</string>
 
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index e3033e7..ff3801f 100755
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -815,6 +815,8 @@
   <java-symbol type="string" name="mobile_provisioning_url" />
   <java-symbol type="string" name="mobile_redirected_provisioning_url" />
   <java-symbol type="string" name="quick_contacts_not_available" />
+  <java-symbol type="string" name="reboot_to_recovery_progress" />
+  <java-symbol type="string" name="reboot_to_recovery_title" />
   <java-symbol type="string" name="reboot_safemode_confirm" />
   <java-symbol type="string" name="reboot_safemode_title" />
   <java-symbol type="string" name="relationTypeAssistant" />
diff --git a/docs/html/preview/api-overview.jd b/docs/html/preview/api-overview.jd
index 5fd6bb8..b8a6033 100644
--- a/docs/html/preview/api-overview.jd
+++ b/docs/html/preview/api-overview.jd
@@ -165,11 +165,11 @@
 
 <p>To see an app implementation of this feature, refer to the
 <a href="https://github.com/googlesamples/android-ConfirmCredentials" class="external-link">
-  Confirm Device Credentials sample</a>.</p>
+  Confirm Credentials sample</a>.</p>
 
 <h2 id="direct-share">Direct Share</h2>
 
-<img src="{@docRoot}preview/images/direct-share-screen_2x.png"
+<img src="{@docRoot}preview/images/direct-share-screen.png"
 srcset="{@docRoot}preview/images/direct-share-screen.png 1x, preview/images/direct-share-screen_2x.png 2x"
 style="float:right; margin:0 0 20px 30px" width="312" height="335" />
 
diff --git a/docs/html/preview/testing/performance.jd b/docs/html/preview/testing/performance.jd
index 4ac5a90..003b619 100644
--- a/docs/html/preview/testing/performance.jd
+++ b/docs/html/preview/testing/performance.jd
@@ -554,10 +554,10 @@
 </h4>
 
 <p>
-  Tool suites like <a href=
-  "https://developer.android.com/tools/testing-support-library/index.html">UIAutomator</a>,
-  and <a href="https://code.google.com/p/android-test-kit/">Espresso</a> are built to help
-  automate the action of a user moving through your application. These are simple
+  Tool suites, like <a href=
+  "{@docRoot}training/testing/ui-testing/uiautomator-testing.html">UI Automator</a> and
+  <a href="{@docRoot}training/testing/ui-testing/espresso-testing.html">Espresso</a>, are
+  built to help automate the action of a user moving through your application. These are simple
   frameworks which mimic user interaction with your device. To use these frameworks, you
   effectively create unique scripts, which run through a set of user-actions, and play them
   out on the device itself.
@@ -585,7 +585,7 @@
 
 <p>
   It’s worth noting that UI testing frameworks (like <a href=
-  "https://developer.android.com/tools/testing-support-library/index.html">UIAutomator</a>)
+  "{@docRoot}training/testing/ui-testing/uiautomator-testing.html">UI Automator</a>)
   run on the target device/emulator directly. While performance gathering information done
   by <em>dumpsys gfxinfo</em> is driven by a host machine, sending commands over ADB. To
   help bridge the automation of these separate entities, <a href=
@@ -595,7 +595,7 @@
 </p>
 
 <p>
-  Building a set of scripts for proper Automation of UI Performance testing, at a minimum,
+  Building a set of scripts for proper automation of UI Performance testing, at a minimum,
   should be able to utilize monkeyRunner to accomplish the following tasks:
 </p>
 
@@ -603,7 +603,7 @@
   <li>Load &amp; Launch a desired APK to a target device, devices, or emulator.
   </li>
 
-  <li>Launch a UIAutomator UI test, and allow it to be executed
+  <li>Launch a UI Automator UI test, and allow it to be executed
   </li>
 
   <li>Collect performance information through <em>dumpsys gfxinfo</em><em>.</em>
diff --git a/libs/hwui/CanvasState.cpp b/libs/hwui/CanvasState.cpp
index e88e9f6..e22b0d3 100644
--- a/libs/hwui/CanvasState.cpp
+++ b/libs/hwui/CanvasState.cpp
@@ -189,6 +189,9 @@
     mSnapshot->setClippingRoundRect(allocator, rect, radius, highPriority);
 }
 
+void CanvasState::setProjectionPathMask(LinearAllocator& allocator, const SkPath* path) {
+    mSnapshot->setProjectionPathMask(allocator, path);
+}
 
 ///////////////////////////////////////////////////////////////////////////////
 // Quick Rejection
diff --git a/libs/hwui/CanvasState.h b/libs/hwui/CanvasState.h
index 8e4a4d3..9354e94 100644
--- a/libs/hwui/CanvasState.h
+++ b/libs/hwui/CanvasState.h
@@ -130,6 +130,7 @@
     void setClippingOutline(LinearAllocator& allocator, const Outline* outline);
     void setClippingRoundRect(LinearAllocator& allocator,
             const Rect& rect, float radius, bool highPriority = true);
+    void setProjectionPathMask(LinearAllocator& allocator, const SkPath* path);
 
     /**
      * Returns true if drawing in the rectangle (left, top, right, bottom)
diff --git a/libs/hwui/DeferredDisplayList.cpp b/libs/hwui/DeferredDisplayList.cpp
index 6fcf958..b077a85 100644
--- a/libs/hwui/DeferredDisplayList.cpp
+++ b/libs/hwui/DeferredDisplayList.cpp
@@ -195,6 +195,7 @@
         // Identical round rect clip state means both ops will clip in the same way, or not at all.
         // As the state objects are const, we can compare their pointers to determine mergeability
         if (lhs->mRoundRectClipState != rhs->mRoundRectClipState) return false;
+        if (lhs->mProjectionPathMask != rhs->mProjectionPathMask) return false;
 
         /* Clipping compatibility check
          *
diff --git a/libs/hwui/DeferredDisplayList.h b/libs/hwui/DeferredDisplayList.h
index 3d0ca6d..160c1ad 100644
--- a/libs/hwui/DeferredDisplayList.h
+++ b/libs/hwui/DeferredDisplayList.h
@@ -63,6 +63,7 @@
     mat4 mMatrix;
     float mAlpha;
     const RoundRectClipState* mRoundRectClipState;
+    const ProjectionPathMask* mProjectionPathMask;
 };
 
 class OpStatePair {
diff --git a/libs/hwui/Matrix.h b/libs/hwui/Matrix.h
index a760135..c152789 100644
--- a/libs/hwui/Matrix.h
+++ b/libs/hwui/Matrix.h
@@ -134,6 +134,12 @@
 
     uint8_t getType() const;
 
+    void multiplyInverse(const Matrix4& v) {
+        Matrix4 inv;
+        inv.loadInverse(v);
+        multiply(inv);
+    }
+
     void multiply(const Matrix4& v) {
         Matrix4 u;
         u.loadMultiply(*this, v);
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index b3fb7ef..8f91620 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -40,6 +40,7 @@
 
 #include <SkCanvas.h>
 #include <SkColor.h>
+#include <SkPathOps.h>
 #include <SkShader.h>
 #include <SkTypeface.h>
 
@@ -1193,8 +1194,9 @@
     state.mMatrix.load(*currentMatrix);
     state.mAlpha = currentSnapshot()->alpha;
 
-    // always store/restore, since it's just a pointer
+    // always store/restore, since these are just pointers
     state.mRoundRectClipState = currentSnapshot()->roundRectClipState;
+    state.mProjectionPathMask = currentSnapshot()->projectionPathMask;
     return false;
 }
 
@@ -1202,6 +1204,7 @@
     setMatrix(state.mMatrix);
     writableSnapshot()->alpha = state.mAlpha;
     writableSnapshot()->roundRectClipState = state.mRoundRectClipState;
+    writableSnapshot()->projectionPathMask = state.mProjectionPathMask;
 
     if (state.mClipValid && !skipClipRestore) {
         writableSnapshot()->setClip(state.mClip.left, state.mClip.top,
@@ -1758,6 +1761,7 @@
 void OpenGLRenderer::drawConvexPath(const SkPath& path, const SkPaint* paint) {
     VertexBuffer vertexBuffer;
     // TODO: try clipping large paths to viewport
+
     PathTessellator::tessellatePath(path, paint, *currentTransform(), vertexBuffer);
     drawVertexBuffer(vertexBuffer, paint);
 }
@@ -1864,19 +1868,41 @@
             || PaintUtils::paintWillNotDraw(*p)) {
         return;
     }
+
     if (p->getPathEffect() != nullptr) {
         mCaches.textureState().activateTexture(0);
         PathTexture* texture = mCaches.pathCache.getCircle(radius, p);
         drawShape(x - radius, y - radius, texture, p);
-    } else {
-        SkPath path;
-        if (p->getStyle() == SkPaint::kStrokeAndFill_Style) {
-            path.addCircle(x, y, radius + p->getStrokeWidth() / 2);
-        } else {
-            path.addCircle(x, y, radius);
-        }
-        drawConvexPath(path, p);
+        return;
     }
+
+    SkPath path;
+    if (p->getStyle() == SkPaint::kStrokeAndFill_Style) {
+        path.addCircle(x, y, radius + p->getStrokeWidth() / 2);
+    } else {
+        path.addCircle(x, y, radius);
+    }
+
+    if (CC_UNLIKELY(currentSnapshot()->projectionPathMask != nullptr)) {
+        // mask ripples with projection mask
+        SkPath maskPath = *(currentSnapshot()->projectionPathMask->projectionMask);
+
+        Matrix4 screenSpaceTransform;
+        currentSnapshot()->buildScreenSpaceTransform(&screenSpaceTransform);
+
+        Matrix4 totalTransform;
+        totalTransform.loadInverse(screenSpaceTransform);
+        totalTransform.multiply(currentSnapshot()->projectionPathMask->projectionMaskTransform);
+
+        SkMatrix skTotalTransform;
+        totalTransform.copyTo(skTotalTransform);
+        maskPath.transform(skTotalTransform);
+
+        // Mask the ripple path by the projection mask, now that it's
+        // in local space. Note that this can create CCW paths.
+        Op(path, maskPath, kIntersect_PathOp, &path);
+    }
+    drawConvexPath(path, p);
 }
 
 void OpenGLRenderer::drawOval(float left, float top, float right, float bottom,
@@ -2149,6 +2175,10 @@
     mState.setClippingRoundRect(allocator, rect, radius, highPriority);
 }
 
+void OpenGLRenderer::setProjectionPathMask(LinearAllocator& allocator, const SkPath* path) {
+    mState.setProjectionPathMask(allocator, path);
+}
+
 void OpenGLRenderer::drawText(const char* text, int bytesCount, int count, float x, float y,
         const float* positions, const SkPaint* paint, float totalAdvance, const Rect& bounds,
         DrawOpMode drawOpMode) {
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index 218818d..8dae82c 100755
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -399,6 +399,7 @@
     void setClippingOutline(LinearAllocator& allocator, const Outline* outline);
     void setClippingRoundRect(LinearAllocator& allocator,
             const Rect& rect, float radius, bool highPriority = true);
+    void setProjectionPathMask(LinearAllocator& allocator, const SkPath* path);
 
     inline bool hasRectToRectTransform() const { return mState.hasRectToRectTransform(); }
     inline const mat4* currentTransform() const { return mState.currentTransform(); }
diff --git a/libs/hwui/PathTessellator.cpp b/libs/hwui/PathTessellator.cpp
index c1f61d6..e7c6c05 100644
--- a/libs/hwui/PathTessellator.cpp
+++ b/libs/hwui/PathTessellator.cpp
@@ -37,6 +37,7 @@
 
 #include <SkPath.h>
 #include <SkPaint.h>
+#include <SkPoint.h>
 #include <SkGeometry.h> // WARNING: Internal Skia Header
 
 #include <stdlib.h>
@@ -912,6 +913,39 @@
     Vertex::set(newVertex, x, y);
 }
 
+class ClockwiseEnforcer {
+public:
+    void addPoint(const SkPoint& point) {
+        double x = point.x();
+        double y = point.y();
+
+        if (initialized) {
+            sum += (x + lastX) * (y - lastY);
+        } else {
+            initialized = true;
+        }
+
+        lastX = x;
+        lastY = y;
+    }
+    void reverseVectorIfNotClockwise(Vector<Vertex>& vertices) {
+        if (sum < 0) {
+            // negative sum implies CounterClockwise
+            const int size = vertices.size();
+            for (int i = 0; i < size / 2; i++) {
+                Vertex tmp = vertices[i];
+                int k = size - 1 - i;
+                vertices.replaceAt(vertices[k], i);
+                vertices.replaceAt(tmp, k);
+            }
+        }
+    }
+private:
+    bool initialized = false;
+    double lastX, lastY;
+    double sum = 0;
+};
+
 bool PathTessellator::approximatePathOutlineVertices(const SkPath& path, bool forceClose,
         float sqrInvScaleX, float sqrInvScaleY, float thresholdSquared,
         Vector<Vertex>& outputVertices) {
@@ -922,18 +956,22 @@
     SkPath::Iter iter(path, forceClose);
     SkPoint pts[4];
     SkPath::Verb v;
+    ClockwiseEnforcer clockwiseEnforcer;
     while (SkPath::kDone_Verb != (v = iter.next(pts))) {
             switch (v) {
             case SkPath::kMove_Verb:
                 pushToVector(outputVertices, pts[0].x(), pts[0].y());
                 ALOGV("Move to pos %f %f", pts[0].x(), pts[0].y());
+                clockwiseEnforcer.addPoint(pts[0]);
                 break;
             case SkPath::kClose_Verb:
                 ALOGV("Close at pos %f %f", pts[0].x(), pts[0].y());
+                clockwiseEnforcer.addPoint(pts[0]);
                 break;
             case SkPath::kLine_Verb:
                 ALOGV("kLine_Verb %f %f -> %f %f", pts[0].x(), pts[0].y(), pts[1].x(), pts[1].y());
                 pushToVector(outputVertices, pts[1].x(), pts[1].y());
+                clockwiseEnforcer.addPoint(pts[1]);
                 break;
             case SkPath::kQuad_Verb:
                 ALOGV("kQuad_Verb");
@@ -942,6 +980,8 @@
                         pts[2].x(), pts[2].y(),
                         pts[1].x(), pts[1].y(),
                         sqrInvScaleX, sqrInvScaleY, thresholdSquared, outputVertices);
+                clockwiseEnforcer.addPoint(pts[1]);
+                clockwiseEnforcer.addPoint(pts[2]);
                 break;
             case SkPath::kCubic_Verb:
                 ALOGV("kCubic_Verb");
@@ -951,6 +991,9 @@
                         pts[3].x(), pts[3].y(),
                         pts[2].x(), pts[2].y(),
                         sqrInvScaleX, sqrInvScaleY, thresholdSquared, outputVertices);
+                clockwiseEnforcer.addPoint(pts[1]);
+                clockwiseEnforcer.addPoint(pts[2]);
+                clockwiseEnforcer.addPoint(pts[3]);
                 break;
             case SkPath::kConic_Verb: {
                 ALOGV("kConic_Verb");
@@ -965,6 +1008,8 @@
                             quads[offset+1].x(), quads[offset+1].y(),
                             sqrInvScaleX, sqrInvScaleY, thresholdSquared, outputVertices);
                 }
+                clockwiseEnforcer.addPoint(pts[1]);
+                clockwiseEnforcer.addPoint(pts[2]);
                 break;
             }
             default:
@@ -972,13 +1017,17 @@
             }
     }
 
+    bool wasClosed = false;
     int size = outputVertices.size();
     if (size >= 2 && outputVertices[0].x == outputVertices[size - 1].x &&
             outputVertices[0].y == outputVertices[size - 1].y) {
         outputVertices.pop();
-        return true;
+        wasClosed = true;
     }
-    return false;
+
+    // ensure output vector is clockwise
+    clockwiseEnforcer.reverseVectorIfNotClockwise(outputVertices);
+    return wasClosed;
 }
 
 ///////////////////////////////////////////////////////////////////////////////
diff --git a/libs/hwui/PathTessellator.h b/libs/hwui/PathTessellator.h
index 8ac9a3b..ccae65b 100644
--- a/libs/hwui/PathTessellator.h
+++ b/libs/hwui/PathTessellator.h
@@ -82,7 +82,7 @@
             const mat4& transform, VertexBuffer& vertexBuffer);
 
     /**
-     * Approximates a convex, CW outline into a Vector of 2d vertices.
+     * Approximates a convex outline into a clockwise Vector of 2d vertices.
      *
      * @param path The outline to be approximated
      * @param thresholdSquared The threshold of acceptable error (in pixels) when approximating
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index 9e5ec28..7d3b41e 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -769,31 +769,9 @@
     const RenderProperties& backgroundProps = backgroundOp->mRenderNode->properties();
     renderer.translate(backgroundProps.getTranslationX(), backgroundProps.getTranslationY());
 
-    // If the projection reciever has an outline, we mask each of the projected rendernodes to it
-    // Either with clipRect, or special saveLayer masking
-    if (projectionReceiverOutline != nullptr) {
-        const SkRect& outlineBounds = projectionReceiverOutline->getBounds();
-        if (projectionReceiverOutline->isRect(nullptr)) {
-            // mask to the rect outline simply with clipRect
-            ClipRectOp* clipOp = new (alloc) ClipRectOp(
-                    outlineBounds.left(), outlineBounds.top(),
-                    outlineBounds.right(), outlineBounds.bottom(), SkRegion::kIntersect_Op);
-            handler(clipOp, PROPERTY_SAVECOUNT, properties().getClipToBounds());
-        } else {
-            // wrap the projected RenderNodes with a SaveLayer that will mask to the outline
-            SaveLayerOp* op = new (alloc) SaveLayerOp(
-                    outlineBounds.left(), outlineBounds.top(),
-                    outlineBounds.right(), outlineBounds.bottom(),
-                    255, SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag | SkCanvas::kARGB_ClipLayer_SaveFlag);
-            op->setMask(projectionReceiverOutline);
-            handler(op, PROPERTY_SAVECOUNT, properties().getClipToBounds());
-
-            /* TODO: add optimizations here to take advantage of placement/size of projected
-             * children (which may shrink saveLayer area significantly). This is dependent on
-             * passing actual drawing/dirtying bounds of projected content down to native.
-             */
-        }
-    }
+    // If the projection reciever has an outline, we mask projected content to it
+    // (which we know, apriori, are all tessellated paths)
+    renderer.setProjectionPathMask(alloc, projectionReceiverOutline);
 
     // draw projected nodes
     for (size_t i = 0; i < mProjectedNodes.size(); i++) {
@@ -808,10 +786,8 @@
         renderer.restoreToCount(restoreTo);
     }
 
-    if (projectionReceiverOutline != nullptr) {
-        handler(new (alloc) RestoreToCountOp(restoreTo),
-                PROPERTY_SAVECOUNT, properties().getClipToBounds());
-    }
+    handler(new (alloc) RestoreToCountOp(restoreTo),
+            PROPERTY_SAVECOUNT, properties().getClipToBounds());
 }
 
 /**
diff --git a/libs/hwui/ShadowTessellator.cpp b/libs/hwui/ShadowTessellator.cpp
index fb28531..024ff10 100644
--- a/libs/hwui/ShadowTessellator.cpp
+++ b/libs/hwui/ShadowTessellator.cpp
@@ -158,71 +158,6 @@
     }
     return result;
 }
-/**
- * Test whether the polygon is order in clockwise.
- *
- * @param polygon the polygon as a Vector2 array
- * @param len the number of points of the polygon
- */
-bool ShadowTessellator::isClockwise(const Vector2* polygon, int len) {
-    if (len < 2 || polygon == nullptr) {
-        return true;
-    }
-    double sum = 0;
-    double p1x = polygon[len - 1].x;
-    double p1y = polygon[len - 1].y;
-    for (int i = 0; i < len; i++) {
-
-        double p2x = polygon[i].x;
-        double p2y = polygon[i].y;
-        sum += p1x * p2y - p2x * p1y;
-        p1x = p2x;
-        p1y = p2y;
-    }
-    return sum < 0;
-}
-
-bool ShadowTessellator::isClockwisePath(const SkPath& path) {
-    SkPath::Iter iter(path, false);
-    SkPoint pts[4];
-    SkPath::Verb v;
-
-    Vector<Vector2> arrayForDirection;
-    while (SkPath::kDone_Verb != (v = iter.next(pts))) {
-            switch (v) {
-            case SkPath::kMove_Verb:
-                arrayForDirection.add((Vector2){pts[0].x(), pts[0].y()});
-                break;
-            case SkPath::kLine_Verb:
-                arrayForDirection.add((Vector2){pts[1].x(), pts[1].y()});
-                break;
-            case SkPath::kConic_Verb:
-            case SkPath::kQuad_Verb:
-                arrayForDirection.add((Vector2){pts[1].x(), pts[1].y()});
-                arrayForDirection.add((Vector2){pts[2].x(), pts[2].y()});
-                break;
-            case SkPath::kCubic_Verb:
-                arrayForDirection.add((Vector2){pts[1].x(), pts[1].y()});
-                arrayForDirection.add((Vector2){pts[2].x(), pts[2].y()});
-                arrayForDirection.add((Vector2){pts[3].x(), pts[3].y()});
-                break;
-            default:
-                break;
-            }
-    }
-
-    return isClockwise(arrayForDirection.array(), arrayForDirection.size());
-}
-
-void ShadowTessellator::reverseVertexArray(Vertex* polygon, int len) {
-    int n = len / 2;
-    for (int i = 0; i < n; i++) {
-        Vertex tmp = polygon[i];
-        int k = len - 1 - i;
-        polygon[i] = polygon[k];
-        polygon[k] = tmp;
-    }
-}
 
 int ShadowTessellator::getExtraVertexNumber(const Vector2& vector1,
         const Vector2& vector2, float divisor) {
diff --git a/libs/hwui/ShadowTessellator.h b/libs/hwui/ShadowTessellator.h
index c04d8ef..5f4c9c5 100644
--- a/libs/hwui/ShadowTessellator.h
+++ b/libs/hwui/ShadowTessellator.h
@@ -83,23 +83,6 @@
     static bool isClockwise(const Vector2* polygon, int len);
 
     static Vector2 calculateNormal(const Vector2& p1, const Vector2& p2);
-    /**
-     * Determine whether the path is clockwise, using the control points.
-     *
-     * TODO: Given the skia is using inverted Y coordinate, shadow system needs
-     * to convert to the same coordinate to avoid the extra reverse.
-     *
-     * @param path The path to be examined.
-     */
-    static bool isClockwisePath(const SkPath &path);
-
-    /**
-     * Reverse the vertex array.
-     *
-     * @param polygon The vertex array to be reversed.
-     * @param len The length of the vertex array.
-     */
-    static void reverseVertexArray(Vertex* polygon, int len);
 
     static int getExtraVertexNumber(const Vector2& vector1, const Vector2& vector2,
             float divisor);
diff --git a/libs/hwui/Snapshot.cpp b/libs/hwui/Snapshot.cpp
index 9e7faee..beb2e1d 100644
--- a/libs/hwui/Snapshot.cpp
+++ b/libs/hwui/Snapshot.cpp
@@ -36,6 +36,7 @@
         , empty(false)
         , alpha(1.0f)
         , roundRectClipState(nullptr)
+        , projectionPathMask(nullptr)
         , mClipArea(&mClipAreaRoot) {
     transform = &mTransformRoot;
     region = nullptr;
@@ -54,6 +55,7 @@
         , empty(false)
         , alpha(s->alpha)
         , roundRectClipState(s->roundRectClipState)
+        , projectionPathMask(s->projectionPathMask)
         , mClipArea(nullptr)
         , mViewportData(s->mViewportData)
         , mRelativeLightCenter(s->mRelativeLightCenter) {
@@ -141,6 +143,34 @@
     transform->loadTranslate(x, y, z);
 }
 
+void Snapshot::buildScreenSpaceTransform(Matrix4* outTransform) const {
+    // build (reverse ordered) list of the stack of snapshots, terminated with a NULL
+    Vector<const Snapshot*> snapshotList;
+    snapshotList.push(nullptr);
+    const Snapshot* current = this;
+    do {
+        snapshotList.push(current);
+        current = current->previous.get();
+    } while (current);
+
+    // traverse the list, adding in each transform that contributes to the total transform
+    outTransform->loadIdentity();
+    for (size_t i = snapshotList.size() - 1; i > 0; i--) {
+        // iterate down the stack
+        const Snapshot* current = snapshotList[i];
+        const Snapshot* next = snapshotList[i - 1];
+        if (current->flags & kFlagIsFboLayer) {
+            // if we've hit a layer, translate by the layer's draw offset
+            outTransform->translate(current->layer->layer.left, current->layer->layer.top);
+        }
+        if (!next || (next->flags & kFlagIsFboLayer)) {
+            // if this snapshot is last, or if this snapshot is last before an
+            // FBO layer (which reset the transform), apply it
+            outTransform->multiply(*(current->transform));
+        }
+    }
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 // Clipping round rect
 ///////////////////////////////////////////////////////////////////////////////
@@ -191,6 +221,18 @@
     roundRectClipState = state;
 }
 
+void Snapshot::setProjectionPathMask(LinearAllocator& allocator, const SkPath* path) {
+    if (path) {
+        ProjectionPathMask* mask = new (allocator) ProjectionPathMask;
+        mask->projectionMask = path;
+        buildScreenSpaceTransform(&(mask->projectionMaskTransform));
+
+        projectionPathMask = mask;
+    } else {
+        projectionPathMask = nullptr;
+    }
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 // Queries
 ///////////////////////////////////////////////////////////////////////////////
diff --git a/libs/hwui/Snapshot.h b/libs/hwui/Snapshot.h
index 4d704ab..af6ad72 100644
--- a/libs/hwui/Snapshot.h
+++ b/libs/hwui/Snapshot.h
@@ -63,6 +63,17 @@
     float radius;
 };
 
+class ProjectionPathMask {
+public:
+    /** static void* operator new(size_t size); PURPOSELY OMITTED, allocator only **/
+    static void* operator new(size_t size, LinearAllocator& allocator) {
+        return allocator.alloc(size);
+    }
+
+    const SkPath* projectionMask;
+    Matrix4 projectionMaskTransform;
+};
+
 /**
  * A snapshot holds information about the current state of the rendering
  * surface. A snapshot is usually created whenever the user calls save()
@@ -190,6 +201,11 @@
             float radius, bool highPriority);
 
     /**
+     * Sets (and replaces) the current projection mask
+     */
+    void setProjectionPathMask(LinearAllocator& allocator, const SkPath* path);
+
+    /**
      * Indicates whether this snapshot should be ignored. A snapshot
      * is typically ignored if its layer is invisible or empty.
      */
@@ -201,6 +217,12 @@
     bool hasPerspectiveTransform() const;
 
     /**
+     * Fills outTransform with the current, total transform to screen space,
+     * across layer boundaries.
+     */
+    void buildScreenSpaceTransform(Matrix4* outTransform) const;
+
+    /**
      * Dirty flags.
      */
     int flags;
@@ -272,6 +294,11 @@
      */
     const RoundRectClipState* roundRectClipState;
 
+    /**
+     * Current projection masking path - used exclusively to mask tessellated circles.
+     */
+    const ProjectionPathMask* projectionPathMask;
+
     void dump() const;
 
 private:
diff --git a/libs/hwui/TessellationCache.cpp b/libs/hwui/TessellationCache.cpp
index fc173f7..704a691 100644
--- a/libs/hwui/TessellationCache.cpp
+++ b/libs/hwui/TessellationCache.cpp
@@ -207,6 +207,16 @@
     transformXY->mapPoint(point.x, point.y);
 }
 
+static void reverseVertexArray(Vertex* polygon, int len) {
+    int n = len / 2;
+    for (int i = 0; i < n; i++) {
+        Vertex tmp = polygon[i];
+        int k = len - 1 - i;
+        polygon[i] = polygon[k];
+        polygon[k] = tmp;
+    }
+}
+
 static void tessellateShadows(
         const Matrix4* drawTransform, const Rect* localClip,
         bool isCasterOpaque, const SkPath* casterPerimeter,
@@ -219,10 +229,9 @@
     const float casterRefinementThresholdSquared = 4.0f;
     PathTessellator::approximatePathOutlineVertices(*casterPerimeter,
             casterRefinementThresholdSquared, casterVertices2d);
-    if (!ShadowTessellator::isClockwisePath(*casterPerimeter)) {
-        ShadowTessellator::reverseVertexArray(casterVertices2d.editArray(),
-                casterVertices2d.size());
-    }
+
+    // Shadow requires CCW for now. TODO: remove potential double-reverse
+    reverseVertexArray(casterVertices2d.editArray(), casterVertices2d.size());
 
     if (casterVertices2d.size() == 0) return;
 
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 6e1b434..18954e6 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -39,6 +39,8 @@
 import android.Manifest;
 import android.app.AppOpsManager;
 import android.app.ApplicationThreadNative;
+import android.app.AssistContent;
+import android.app.AssistStructure;
 import android.app.IActivityContainer;
 import android.app.IActivityContainerCallback;
 import android.app.IAppTask;
@@ -360,6 +362,10 @@
     // to respond with the result.
     static final int PENDING_ASSIST_EXTRAS_TIMEOUT = 500;
 
+    // How long top wait when going through the modern assist (which doesn't need to block
+    // on getting this result before starting to launch its UI).
+    static final int PENDING_ASSIST_EXTRAS_LONG_TIMEOUT = 2000;
+
     // Maximum number of persisted Uri grants a package is allowed
     static final int MAX_PERSISTED_URI_GRANTS = 128;
 
@@ -477,6 +483,8 @@
         public final int userHandle;
         public boolean haveResult = false;
         public Bundle result = null;
+        public AssistStructure structure = null;
+        public AssistContent content = null;
         public PendingAssistExtras(ActivityRecord _activity, Bundle _extras, Intent _intent,
                 String _hint, IResultReceiver _receiver, int _userHandle) {
             activity = _activity;
@@ -10617,7 +10625,7 @@
     @Override
     public Bundle getAssistContextExtras(int requestType) {
         PendingAssistExtras pae = enqueueAssistContext(requestType, null, null, null,
-                UserHandle.getCallingUserId());
+                UserHandle.getCallingUserId(), PENDING_ASSIST_EXTRAS_TIMEOUT);
         if (pae == null) {
             return null;
         }
@@ -10639,11 +10647,12 @@
 
     @Override
     public void requestAssistContextExtras(int requestType, IResultReceiver receiver) {
-        enqueueAssistContext(requestType, null, null, receiver, UserHandle.getCallingUserId());
+        enqueueAssistContext(requestType, null, null, receiver, UserHandle.getCallingUserId(),
+                PENDING_ASSIST_EXTRAS_LONG_TIMEOUT);
     }
 
     private PendingAssistExtras enqueueAssistContext(int requestType, Intent intent, String hint,
-            IResultReceiver receiver, int userHandle) {
+            IResultReceiver receiver, int userHandle, long timeout) {
         enforceCallingPermission(android.Manifest.permission.GET_TOP_ACTIVITY_INFO,
                 "enqueueAssistContext()");
         synchronized (this) {
@@ -10669,7 +10678,7 @@
                 activity.app.thread.requestAssistContextExtras(activity.appToken, pae,
                         requestType);
                 mPendingAssistExtras.add(pae);
-                mHandler.postDelayed(pae, PENDING_ASSIST_EXTRAS_TIMEOUT);
+                mHandler.postDelayed(pae, timeout);
             } catch (RemoteException e) {
                 Slog.w(TAG, "getAssistContextExtras failed: crash calling " + activity);
                 return null;
@@ -10698,10 +10707,13 @@
         }
     }
 
-    public void reportAssistContextExtras(IBinder token, Bundle extras) {
+    public void reportAssistContextExtras(IBinder token, Bundle extras, AssistStructure structure,
+            AssistContent content) {
         PendingAssistExtras pae = (PendingAssistExtras)token;
         synchronized (pae) {
             pae.result = extras;
+            pae.structure = structure;
+            pae.content = content;
             pae.haveResult = true;
             pae.notifyAll();
             if (pae.intent == null && pae.receiver == null) {
@@ -10721,8 +10733,12 @@
             }
             if (pae.receiver != null) {
                 // Caller wants result sent back to them.
+                Bundle topBundle = new Bundle();
+                topBundle.putBundle("data", pae.extras);
+                topBundle.putParcelable("structure", pae.structure);
+                topBundle.putParcelable("content", pae.content);
                 try {
-                    pae.receiver.send(0, pae.extras);
+                    pae.receiver.send(0, topBundle);
                 } catch (RemoteException e) {
                 }
                 return;
@@ -10741,7 +10757,8 @@
     }
 
     public boolean launchAssistIntent(Intent intent, int requestType, String hint, int userHandle) {
-        return enqueueAssistContext(requestType, intent, hint, null, userHandle) != null;
+        return enqueueAssistContext(requestType, intent, hint, null, userHandle,
+                PENDING_ASSIST_EXTRAS_TIMEOUT) != null;
     }
 
     public void registerProcessObserver(IProcessObserver observer) {
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 5aea746..1b5391e 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -2518,8 +2518,7 @@
     /**
      * Low-level function to reboot the device. On success, this
      * function doesn't return. If more than 20 seconds passes from
-     * the time a reboot is requested (120 seconds for reboot to
-     * recovery), this method returns.
+     * the time a reboot is requested, this method returns.
      *
      * @param reason code to pass to the kernel (e.g. "recovery"), or null.
      */
@@ -2527,27 +2526,21 @@
         if (reason == null) {
             reason = "";
         }
-        long duration;
         if (reason.equals(PowerManager.REBOOT_RECOVERY)) {
             // If we are rebooting to go into recovery, instead of
             // setting sys.powerctl directly we'll start the
             // pre-recovery service which will do some preparation for
             // recovery and then reboot for us.
-            //
-            // This preparation can take more than 20 seconds if
-            // there's a very large update package, so lengthen the
-            // timeout.  We have seen 750MB packages take 3-4 minutes
             SystemProperties.set("ctl.start", "pre-recovery");
-            duration = 300 * 1000L;
         } else {
             SystemProperties.set("sys.powerctl", "reboot," + reason);
-            duration = 20 * 1000L;
         }
         try {
-            Thread.sleep(duration);
+            Thread.sleep(20 * 1000L);
         } catch (InterruptedException e) {
             Thread.currentThread().interrupt();
         }
+        Slog.wtf(TAG, "Unexpected return from lowLevelReboot!");
     }
 
     @Override // Watchdog.Monitor implementation
diff --git a/services/core/java/com/android/server/power/ShutdownThread.java b/services/core/java/com/android/server/power/ShutdownThread.java
index 84eab42..85dd9d4 100644
--- a/services/core/java/com/android/server/power/ShutdownThread.java
+++ b/services/core/java/com/android/server/power/ShutdownThread.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
- 
+
 package com.android.server.power;
 
 import android.app.ActivityManagerNative;
@@ -44,6 +44,8 @@
 import android.os.SystemVibrator;
 import android.os.storage.IMountService;
 import android.os.storage.IMountShutdownObserver;
+import android.system.ErrnoException;
+import android.system.Os;
 
 import com.android.internal.telephony.ITelephony;
 import com.android.server.pm.PackageManagerService;
@@ -51,6 +53,11 @@
 import android.util.Log;
 import android.view.WindowManager;
 
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+
 public final class ShutdownThread extends Thread {
     // constants
     private static final String TAG = "ShutdownThread";
@@ -59,14 +66,18 @@
     private static final int MAX_BROADCAST_TIME = 10*1000;
     private static final int MAX_SHUTDOWN_WAIT_TIME = 20*1000;
     private static final int MAX_RADIO_WAIT_TIME = 12*1000;
+    private static final int MAX_UNCRYPT_WAIT_TIME = 15*60*1000;
 
     // length of vibration before shutting down
     private static final int SHUTDOWN_VIBRATE_MS = 500;
-    
+
     // state tracking
     private static Object sIsStartedGuard = new Object();
     private static boolean sIsStarted = false;
-    
+
+    // uncrypt status file
+    private static final String UNCRYPT_STATUS_FILE = "/cache/recovery/uncrypt_status";
+
     private static boolean mReboot;
     private static boolean mRebootSafeMode;
     private static String mRebootReason;
@@ -94,10 +105,11 @@
     private Handler mHandler;
 
     private static AlertDialog sConfirmDialog;
-    
+    private ProgressDialog mProgressDialog;
+
     private ShutdownThread() {
     }
- 
+
     /**
      * Request a clean shutdown, waiting for subsystems to clean up their
      * state etc.  Must be called from a Looper thread in which its UI
@@ -226,7 +238,11 @@
         // throw up an indeterminate system dialog to indicate radio is
         // shutting down.
         ProgressDialog pd = new ProgressDialog(context);
-        pd.setTitle(context.getText(com.android.internal.R.string.power_off));
+        if (mRebootReason.equals(PowerManager.REBOOT_RECOVERY)) {
+            pd.setTitle(context.getText(com.android.internal.R.string.reboot_to_recovery_title));
+        } else {
+            pd.setTitle(context.getText(com.android.internal.R.string.power_off));
+        }
         pd.setMessage(context.getText(com.android.internal.R.string.shutdown_progress));
         pd.setIndeterminate(true);
         pd.setCancelable(false);
@@ -234,6 +250,7 @@
 
         pd.show();
 
+        sInstance.mProgressDialog = pd;
         sInstance.mContext = context;
         sInstance.mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
 
@@ -307,14 +324,14 @@
         }
 
         Log.i(TAG, "Sending shutdown broadcast...");
-        
+
         // First send the high-level shut down broadcast.
         mActionDone = false;
         Intent intent = new Intent(Intent.ACTION_SHUTDOWN);
         intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
         mContext.sendOrderedBroadcastAsUser(intent,
                 UserHandle.ALL, null, br, mHandler, 0, null, null);
-        
+
         final long endTime = SystemClock.elapsedRealtime() + MAX_BROADCAST_TIME;
         synchronized (mActionDoneSync) {
             while (!mActionDone) {
@@ -329,9 +346,9 @@
                 }
             }
         }
-        
+
         Log.i(TAG, "Shutting down activity manager...");
-        
+
         final IActivityManager am =
             ActivityManagerNative.asInterface(ServiceManager.checkService("activity"));
         if (am != null) {
@@ -390,9 +407,55 @@
             }
         }
 
+        // If it's to reboot into recovery, invoke uncrypt via init service.
+        if (mRebootReason.equals(PowerManager.REBOOT_RECOVERY)) {
+            uncrypt();
+        }
+
         rebootOrShutdown(mContext, mReboot, mRebootReason);
     }
 
+    private void prepareUncryptProgress() {
+        // Reset the dialog message to show the decrypt process.
+        mHandler.post(new Runnable() {
+            @Override
+            public void run() {
+                if (mProgressDialog != null) {
+                    mProgressDialog.dismiss();
+                }
+                // It doesn't work to change the style of the existing
+                // one. Have to create a new one.
+                ProgressDialog pd = new ProgressDialog(mContext);
+
+                pd.setTitle(mContext.getText(
+                        com.android.internal.R.string.reboot_to_recovery_title));
+                pd.setMessage(mContext.getText(
+                        com.android.internal.R.string.reboot_to_recovery_progress));
+                pd.setIndeterminate(false);
+                pd.setMax(100);
+                pd.setCancelable(false);
+                pd.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
+                pd.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
+                pd.setProgressNumberFormat(null);
+                pd.setProgress(0);
+
+                mProgressDialog = pd;
+                mProgressDialog.show();
+            }
+        });
+    }
+
+    private void setUncryptProgress(final int progress) {
+        mHandler.post(new Runnable() {
+            @Override
+            public void run() {
+                if (mProgressDialog != null) {
+                    mProgressDialog.setProgress(progress);
+                }
+            }
+        });
+    }
+
     private void shutdownRadios(int timeout) {
         // If a radio is wedged, disabling it may hang so we do this work in another thread,
         // just in case.
@@ -537,4 +600,78 @@
         Log.i(TAG, "Performing low-level shutdown...");
         PowerManagerService.lowLevelShutdown();
     }
+
+    private void uncrypt() {
+        Log.i(TAG, "Calling uncrypt and monitoring the progress...");
+
+        // Update the ProcessDialog message and style.
+        sInstance.prepareUncryptProgress();
+
+        final boolean[] done = new boolean[1];
+        done[0] = false;
+        Thread t = new Thread() {
+            @Override
+            public void run() {
+                // Create the status pipe file to communicate with /system/bin/uncrypt.
+                new File(UNCRYPT_STATUS_FILE).delete();
+                try {
+                    Os.mkfifo(UNCRYPT_STATUS_FILE, 0600);
+                } catch (ErrnoException e) {
+                    Log.w(TAG, "ErrnoException when creating named pipe \"" + UNCRYPT_STATUS_FILE +
+                            "\": " + e.getMessage());
+                }
+
+                SystemProperties.set("ctl.start", "uncrypt");
+
+                // Read the status from the pipe.
+                try (BufferedReader reader = new BufferedReader(
+                        new FileReader(UNCRYPT_STATUS_FILE))) {
+
+                    int last_status = Integer.MIN_VALUE;
+                    while (true) {
+                        String str = reader.readLine();
+                        try {
+                            int status = Integer.parseInt(str);
+
+                            // Avoid flooding the log with the same message.
+                            if (status == last_status && last_status != Integer.MIN_VALUE) {
+                                continue;
+                            }
+                            last_status = status;
+
+                            if (status >= 0 && status < 100) {
+                                // Update status
+                                Log.d(TAG, "uncrypt read status: " + status);
+                                sInstance.setUncryptProgress(status);
+                            } else if (status == 100) {
+                                Log.d(TAG, "uncrypt successfully finished.");
+                                sInstance.setUncryptProgress(status);
+                                break;
+                            } else {
+                                // Error in /system/bin/uncrypt. Or it's rebooting to recovery
+                                // to perform other operations (e.g. factory reset).
+                                Log.d(TAG, "uncrypt failed with status: " + status);
+                                break;
+                            }
+                        } catch (NumberFormatException unused) {
+                            Log.d(TAG, "uncrypt invalid status received: " + str);
+                            break;
+                        }
+                    }
+                } catch (IOException unused) {
+                    Log.w(TAG, "IOException when reading \"" + UNCRYPT_STATUS_FILE + "\".");
+                }
+                done[0] = true;
+            }
+        };
+        t.start();
+
+        try {
+            t.join(MAX_UNCRYPT_WAIT_TIME);
+        } catch (InterruptedException unused) {
+        }
+        if (!done[0]) {
+            Log.w(TAG, "Timed out waiting for uncrypt.");
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 5d8979f..6042c27 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -466,6 +466,8 @@
     boolean mShowingBootMessages = false;
     boolean mBootAnimationStopped = false;
 
+    /** Dump of the windows and app tokens at the time of the last ANR. Cleared after
+     * LAST_ANR_LIFETIME_DURATION_MSECS */
     String mLastANRState;
 
     /** All DisplayContents in the world, kept here */
@@ -1025,7 +1027,7 @@
     private void placeWindowAfter(WindowState pos, WindowState window) {
         final WindowList windows = pos.getWindowList();
         final int i = windows.indexOf(pos);
-        if (true || DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(
+        if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(
             TAG, "Adding window " + window + " at "
             + (i+1) + " of " + windows.size() + " (after " + pos + ")");
         windows.add(i+1, window);
@@ -1035,7 +1037,7 @@
     private void placeWindowBefore(WindowState pos, WindowState window) {
         final WindowList windows = pos.getWindowList();
         int i = windows.indexOf(pos);
-        if (true || DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(
+        if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(
             TAG, "Adding window " + window + " at "
             + i + " of " + windows.size() + " (before " + pos + ")");
         if (i < 0) {
@@ -1133,7 +1135,7 @@
                     //apptoken note that the window could be a floating window
                     //that was created later or a window at the top of the list of
                     //windows associated with this token.
-                    if (true || DEBUG_FOCUS_LIGHT || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG,
+                    if (DEBUG_FOCUS_LIGHT || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG,
                             "not Base app: Adding window " + win + " at " + (newIdx + 1) + " of " +
                             N);
                     windows.add(newIdx + 1, win);
@@ -1255,7 +1257,7 @@
                 break;
             }
         }
-        if (true || DEBUG_FOCUS_LIGHT || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG,
+        if (DEBUG_FOCUS_LIGHT || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG,
                 "Based on layer: Adding window " + win + " at " + (i + 1) + " of " + N);
         windows.add(i + 1, win);
         mWindowsChanged = true;
@@ -3720,7 +3722,7 @@
             atoken.layoutConfigChanges = (configChanges &
                     (ActivityInfo.CONFIG_SCREEN_SIZE | ActivityInfo.CONFIG_ORIENTATION)) != 0;
             atoken.mLaunchTaskBehind = launchTaskBehind;
-            if (true || DEBUG_TOKEN_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG, "addAppToken: " + atoken
+            if (DEBUG_TOKEN_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG, "addAppToken: " + atoken
                     + " to stack=" + stackId + " task=" + taskId + " at " + addPos);
 
             Task task = mTaskIdToTask.get(taskId);
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
index 03abfba..1117373 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
@@ -20,6 +20,7 @@
 import android.app.ActivityManagerNative;
 import android.app.AppOpsManager;
 import android.app.AssistContent;
+import android.app.AssistStructure;
 import android.app.IActivityManager;
 import android.content.ClipData;
 import android.content.ComponentName;
@@ -291,33 +292,37 @@
             return;
         }
         if (mHaveAssistData) {
+            Bundle assistData;
+            AssistStructure structure;
+            AssistContent content;
             if (mAssistData != null) {
+                assistData = mAssistData.getBundle("data");
+                structure = mAssistData.getParcelable("structure");
+                content = mAssistData.getParcelable("content");
                 int uid = mAssistData.getInt(Intent.EXTRA_ASSIST_UID, -1);
-                if (uid >= 0) {
-                    Bundle assistContext = mAssistData.getBundle(Intent.EXTRA_ASSIST_CONTEXT);
-                    if (assistContext != null) {
-                        AssistContent content = AssistContent.getAssistContent(assistContext);
-                        if (content != null) {
-                            Intent intent = content.getIntent();
-                            if (intent != null) {
-                                ClipData data = intent.getClipData();
-                                if (data != null && Intent.isAccessUriMode(intent.getFlags())) {
-                                    grantClipDataPermissions(data, intent.getFlags(), uid,
-                                            mCallingUid, mSessionComponentName.getPackageName());
-                                }
-                            }
-                            ClipData data = content.getClipData();
-                            if (data != null) {
-                                grantClipDataPermissions(data,
-                                        Intent.FLAG_GRANT_READ_URI_PERMISSION,
-                                        uid, mCallingUid, mSessionComponentName.getPackageName());
-                            }
+                if (uid >= 0 && content != null) {
+                    Intent intent = content.getIntent();
+                    if (intent != null) {
+                        ClipData data = intent.getClipData();
+                        if (data != null && Intent.isAccessUriMode(intent.getFlags())) {
+                            grantClipDataPermissions(data, intent.getFlags(), uid,
+                                    mCallingUid, mSessionComponentName.getPackageName());
                         }
                     }
+                    ClipData data = content.getClipData();
+                    if (data != null) {
+                        grantClipDataPermissions(data,
+                                Intent.FLAG_GRANT_READ_URI_PERMISSION,
+                                uid, mCallingUid, mSessionComponentName.getPackageName());
+                    }
                 }
+            } else {
+                assistData = null;
+                structure = null;
+                content = null;
             }
             try {
-                mSession.handleAssist(mAssistData);
+                mSession.handleAssist(assistData, structure, content);
             } catch (RemoteException e) {
             }
             mAssistData = null;
diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml
index 8531944..10cf5c1 100644
--- a/tests/HwAccelerationTest/AndroidManifest.xml
+++ b/tests/HwAccelerationTest/AndroidManifest.xml
@@ -24,11 +24,11 @@
     <uses-feature android:name="android.hardware.camera" />
     <uses-feature android:name="android.hardware.camera.autofocus" />
 
-    <uses-sdk android:minSdkVersion="11" />
-    
+    <uses-sdk android:minSdkVersion="21" />
+
     <application
         android:label="HwUi"
-        android:hardwareAccelerated="true">
+        android:theme="@android:style/Theme.Material.Light">
 
         <activity
                 android:name="HwTests"
@@ -42,8 +42,7 @@
 
         <activity
                 android:name="PathOpsActivity"
-                android:label="Path/Ops"
-                android:theme="@android:style/Theme.Holo.Light">
+                android:label="Path/Ops">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
                 <category android:name="com.android.test.hwui.TEST" />
@@ -52,8 +51,7 @@
 
         <activity
                 android:name="AssetsAtlasActivity"
-                android:label="Atlas/Framework"
-                android:theme="@android:style/Theme.Holo.Light">
+                android:label="Atlas/Framework">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
                 <category android:name="com.android.test.hwui.TEST" />
@@ -62,8 +60,7 @@
 
         <activity
                 android:name="ScaledTextActivity"
-                android:label="Text/Scaled"
-                android:theme="@android:style/Theme.Holo.Light">
+                android:label="Text/Scaled">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
                 <category android:name="com.android.test.hwui.TEST" />
@@ -72,8 +69,7 @@
 
         <activity
                 android:name="Rotate3dTextActivity"
-                android:label="Text/3D Rotation"
-                android:theme="@android:style/Theme.Holo.Light">
+                android:label="Text/3D Rotation">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
                 <category android:name="com.android.test.hwui.TEST" />
diff --git a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionSession.java b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionSession.java
index 3c5c201..70a6336 100644
--- a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionSession.java
+++ b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionSession.java
@@ -119,11 +119,16 @@
     }
 
     @Override
-    public void onHandleAssist(Bundle assistBundle) {
-        if (assistBundle != null) {
-            parseAssistData(assistBundle);
-        } else {
-            Log.i(TAG, "onHandleAssist: NO ASSIST BUNDLE");
+    public void onHandleAssist(Bundle data, AssistStructure structure, AssistContent content) {
+        mAssistStructure = structure;
+        if (mAssistStructure != null) {
+            if (mAssistVisualizer != null) {
+                mAssistVisualizer.setAssistStructure(mAssistStructure);
+            }
+        }
+        if (content != null) {
+            Log.i(TAG, "Assist intent: " + content.getIntent());
+            Log.i(TAG, "Assist clipdata: " + content.getClipData());
         }
     }
 
@@ -139,29 +144,6 @@
         }
     }
 
-    void parseAssistData(Bundle assistBundle) {
-        if (assistBundle != null) {
-            Bundle assistContext = assistBundle.getBundle(Intent.EXTRA_ASSIST_CONTEXT);
-            if (assistContext != null) {
-                mAssistStructure = AssistStructure.getAssistStructure(assistContext);
-                if (mAssistStructure != null) {
-                    if (mAssistVisualizer != null) {
-                        mAssistVisualizer.setAssistStructure(mAssistStructure);
-                    }
-                }
-                AssistContent content = AssistContent.getAssistContent(assistContext);
-                if (content != null) {
-                    Log.i(TAG, "Assist intent: " + content.getIntent());
-                    Log.i(TAG, "Assist clipdata: " + content.getClipData());
-                }
-                return;
-            }
-        }
-        if (mAssistVisualizer != null) {
-            mAssistVisualizer.clearAssistData();
-        }
-    }
-
     void updateState() {
         if (mState == STATE_IDLE) {
             mTopContent.setVisibility(View.VISIBLE);
diff --git a/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java b/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java
index 76019bf..2e515fb 100644
--- a/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java
+++ b/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java
@@ -45,7 +45,24 @@
 import java.util.Arrays;
 import java.util.Map;
 
-import static com.android.ide.common.rendering.api.RenderResources.*;
+import static android.util.TypedValue.TYPE_ATTRIBUTE;
+import static android.util.TypedValue.TYPE_DIMENSION;
+import static android.util.TypedValue.TYPE_FLOAT;
+import static android.util.TypedValue.TYPE_INT_BOOLEAN;
+import static android.util.TypedValue.TYPE_INT_COLOR_ARGB4;
+import static android.util.TypedValue.TYPE_INT_COLOR_ARGB8;
+import static android.util.TypedValue.TYPE_INT_COLOR_RGB4;
+import static android.util.TypedValue.TYPE_INT_COLOR_RGB8;
+import static android.util.TypedValue.TYPE_INT_DEC;
+import static android.util.TypedValue.TYPE_INT_HEX;
+import static android.util.TypedValue.TYPE_NULL;
+import static android.util.TypedValue.TYPE_REFERENCE;
+import static android.util.TypedValue.TYPE_STRING;
+import static com.android.SdkConstants.PREFIX_RESOURCE_REF;
+import static com.android.SdkConstants.PREFIX_THEME_REF;
+import static com.android.ide.common.rendering.api.RenderResources.REFERENCE_EMPTY;
+import static com.android.ide.common.rendering.api.RenderResources.REFERENCE_NULL;
+import static com.android.ide.common.rendering.api.RenderResources.REFERENCE_UNDEFINED;
 
 /**
  * Custom implementation of TypedArray to handle non compiled resources.
@@ -223,7 +240,7 @@
         String s = getString(index);
         try {
             if (s != null) {
-                return XmlUtils.convertValueToInt(s, defValue);
+                return convertValueToInt(s, defValue);
             }
         } catch (NumberFormatException e) {
             Bridge.getLog().warning(LayoutLog.TAG_RESOURCES_FORMAT,
@@ -764,14 +781,57 @@
     }
 
     @Override
+    @SuppressWarnings("ResultOfMethodCallIgnored")
     public int getType(int index) {
-        if (!hasValue(index)) {
-            return TypedValue.TYPE_NULL;
+        String value = getString(index);
+        if (value == null) {
+            return TYPE_NULL;
         }
-        ResourceValue value = mResourceData[index];
-        ResourceType resourceType = value.getResourceType();
-        return 0;
-        // TODO: fixme.
+        if (value.startsWith(PREFIX_RESOURCE_REF)) {
+            return TYPE_REFERENCE;
+        }
+        if (value.startsWith(PREFIX_THEME_REF)) {
+            return TYPE_ATTRIBUTE;
+        }
+        try {
+            // Don't care about the value. Only called to check if an exception is thrown.
+            convertValueToInt(value, 0);
+            if (value.startsWith("0x") || value.startsWith("0X")) {
+                return TYPE_INT_HEX;
+            }
+            // is it a color?
+            if (value.startsWith("#")) {
+                int length = value.length() - 1;
+                if (length == 3) {  // rgb
+                    return TYPE_INT_COLOR_RGB4;
+                }
+                if (length == 4) {  // argb
+                    return TYPE_INT_COLOR_ARGB4;
+                }
+                if (length == 6) {  // rrggbb
+                    return TYPE_INT_COLOR_RGB8;
+                }
+                if (length == 8) {  // aarrggbb
+                    return TYPE_INT_COLOR_ARGB8;
+                }
+            }
+            if (value.equalsIgnoreCase("true") || value.equalsIgnoreCase("false")) {
+                return TYPE_INT_BOOLEAN;
+            }
+            return TYPE_INT_DEC;
+        } catch (NumberFormatException ignored) {
+            try {
+                Float.parseFloat(value);
+                return TYPE_FLOAT;
+            } catch (NumberFormatException ignore) {
+            }
+            // Might be a dimension.
+            if (ResourceHelper.parseFloatAttribute(null, value, new TypedValue(), false)) {
+                return TYPE_DIMENSION;
+            }
+        }
+        // TODO: handle fractions.
+        return TYPE_STRING;
     }
 
     /**
@@ -883,6 +943,52 @@
         return null;
     }
 
+    /**
+     * Copied from {@link XmlUtils#convertValueToInt(CharSequence, int)}, but adapted to account
+     * for aapt, and the fact that host Java VM's Integer.parseInt("XXXXXXXX", 16) cannot handle
+     * "XXXXXXXX" > 80000000.
+     */
+    private static int convertValueToInt(@Nullable String charSeq, int defValue) {
+        if (null == charSeq)
+            return defValue;
+
+        int sign = 1;
+        int index = 0;
+        int len = charSeq.length();
+        int base = 10;
+
+        if ('-' == charSeq.charAt(0)) {
+            sign = -1;
+            index++;
+        }
+
+        if ('0' == charSeq.charAt(index)) {
+            //  Quick check for a zero by itself
+            if (index == (len - 1))
+                return 0;
+
+            char c = charSeq.charAt(index + 1);
+
+            if ('x' == c || 'X' == c) {
+                index += 2;
+                base = 16;
+            } else {
+                index++;
+                // Leave the base as 10. aapt removes the preceding zero, and thus when framework
+                // sees the value, it only gets the decimal value.
+            }
+        } else if ('#' == charSeq.charAt(index)) {
+            return ResourceHelper.getColor(charSeq) * sign;
+        } else if ("true".equals(charSeq) || "TRUE".equals(charSeq)) {
+            return -1;
+        } else if ("false".equals(charSeq) || "FALSE".equals(charSeq)) {
+            return 0;
+        }
+
+        // Use Long, since we want to handle hex ints > 80000000.
+        return ((int)Long.parseLong(charSeq.substring(index), base)) * sign;
+    }
+
     static TypedArray obtain(Resources res, int len) {
         return res instanceof BridgeResources ?
                 new BridgeTypedArray(((BridgeResources) res), null, len, true) : null;
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
index 9d2b884..f03ec58 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
@@ -130,6 +130,7 @@
         new IdentityHashMap<Object, Map<String,String>>();
 
     // maps for dynamically generated id representing style objects (StyleResourceValue)
+    @Nullable
     private Map<Integer, StyleResourceValue> mDynamicIdToStyleMap;
     private Map<StyleResourceValue, Integer> mStyleToDynamicIdMap;
     private int mDynamicIdGenerator = 0x02030000; // Base id for R.style in custom namespace
@@ -727,7 +728,7 @@
                 }
             }
         } else if (defStyleRes != 0) {
-            StyleResourceValue item = mDynamicIdToStyleMap.get(defStyleRes);
+            StyleResourceValue item = getStyleByDynamicId(defStyleRes);
             if (item != null) {
                 defStyleValues = item;
             } else {