Merge "Add descriptions to MediaController buttons" into lmp-dev
diff --git a/api/current.txt b/api/current.txt
index 5cafd6c..5710415 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -590,7 +590,6 @@
     field public static final int fillBefore = 16843196; // 0x10101bc
     field public static final int fillColor = 16843807; // 0x101041f
     field public static final int fillEnabled = 16843343; // 0x101024f
-    field public static final int fillOpacity = 16843806; // 0x101041e
     field public static final int fillViewport = 16843130; // 0x101017a
     field public static final int filter = 16843035; // 0x101011b
     field public static final int filterTouchesWhenObscured = 16843460; // 0x10102c4
@@ -1190,7 +1189,6 @@
     field public static final int strokeLineCap = 16843815; // 0x1010427
     field public static final int strokeLineJoin = 16843816; // 0x1010428
     field public static final int strokeMiterLimit = 16843817; // 0x1010429
-    field public static final int strokeOpacity = 16843810; // 0x1010422
     field public static final int strokeWidth = 16843811; // 0x1010423
     field public static final int submitBackground = 16843914; // 0x101048a
     field public static final int subtitle = 16843473; // 0x10102d1
@@ -8680,6 +8678,7 @@
     field public static final int FAILURE_CONFLICT = 2; // 0x2
     field public static final int FAILURE_INCOMPATIBLE = 4; // 0x4
     field public static final int FAILURE_INVALID = 1; // 0x1
+    field public static final int FAILURE_REJECTED = 5; // 0x5
     field public static final int FAILURE_STORAGE = 3; // 0x3
     field public static final int FAILURE_UNKNOWN = 0; // 0x0
   }
@@ -37498,6 +37497,7 @@
     method public void clearChoices();
     method public void clearTextFilter();
     method public void deferNotifyDataSetChanged();
+    method public void fling(int);
     method public int getCacheColorHint();
     method public int getCheckedItemCount();
     method public long[] getCheckedItemIds();
diff --git a/core/java/android/app/backup/BackupManager.java b/core/java/android/app/backup/BackupManager.java
index df0365e..e9297b9 100644
--- a/core/java/android/app/backup/BackupManager.java
+++ b/core/java/android/app/backup/BackupManager.java
@@ -161,11 +161,14 @@
         return result;
     }
 
+    // system APIs start here
+
     /**
      * Begin the process of restoring data from backup.  See the
      * {@link android.app.backup.RestoreSession} class for documentation on that process.
      * @hide
      */
+    @SystemApi
     public RestoreSession beginRestoreSession() {
         RestoreSession session = null;
         checkServiceBinder();
@@ -183,8 +186,6 @@
         return session;
     }
 
-    // system APIs start here
-
     /**
      * Enable/disable the backup service entirely.  When disabled, no backup
      * or restore operations will take place.  Data-changed notifications will
diff --git a/core/java/android/content/pm/IPackageInstaller.aidl b/core/java/android/content/pm/IPackageInstaller.aidl
index cc0d569..5223476 100644
--- a/core/java/android/content/pm/IPackageInstaller.aidl
+++ b/core/java/android/content/pm/IPackageInstaller.aidl
@@ -36,4 +36,6 @@
 
     void uninstall(String packageName, int flags, in IPackageDeleteObserver2 observer, int userId);
     void uninstallSplit(String packageName, String splitName, int flags, in IPackageDeleteObserver2 observer, int userId);
+
+    void setPermissionsResult(int sessionId, boolean accepted);
 }
diff --git a/core/java/android/content/pm/InstallSessionInfo.java b/core/java/android/content/pm/InstallSessionInfo.java
index f263885..161bcde 100644
--- a/core/java/android/content/pm/InstallSessionInfo.java
+++ b/core/java/android/content/pm/InstallSessionInfo.java
@@ -16,7 +16,6 @@
 
 package android.content.pm;
 
-import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Intent;
 import android.graphics.Bitmap;
@@ -33,8 +32,12 @@
     /** {@hide} */
     public String installerPackageName;
     /** {@hide} */
+    public String resolvedBaseCodePath;
+    /** {@hide} */
     public float progress;
     /** {@hide} */
+    public boolean sealed;
+    /** {@hide} */
     public boolean open;
 
     /** {@hide} */
@@ -56,7 +59,9 @@
     public InstallSessionInfo(Parcel source) {
         sessionId = source.readInt();
         installerPackageName = source.readString();
+        resolvedBaseCodePath = source.readString();
         progress = source.readFloat();
+        sealed = source.readInt() != 0;
         open = source.readInt() != 0;
 
         mode = source.readInt();
@@ -149,7 +154,9 @@
     public void writeToParcel(Parcel dest, int flags) {
         dest.writeInt(sessionId);
         dest.writeString(installerPackageName);
+        dest.writeString(resolvedBaseCodePath);
         dest.writeFloat(progress);
+        dest.writeInt(sealed ? 1 : 0);
         dest.writeInt(open ? 1 : 0);
 
         dest.writeInt(mode);
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 01c080d..525142b 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -81,6 +81,10 @@
     @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
     public static final String ACTION_SESSION_DETAILS = "android.content.pm.action.SESSION_DETAILS";
 
+    /** {@hide} */
+    public static final String
+            ACTION_CONFIRM_PERMISSIONS = "android.content.pm.action.CONFIRM_PERMISSIONS";
+
     /**
      * An integer session ID.
      *
@@ -206,6 +210,15 @@
         }
     }
 
+    /** {@hide} */
+    public void setPermissionsResult(int sessionId, boolean accepted) {
+        try {
+            mInstaller.setPermissionsResult(sessionId, accepted);
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
     /**
      * Events for observing session lifecycle.
      * <p>
@@ -603,9 +616,8 @@
          * permission, incompatible certificates, etc. The user may be able to
          * uninstall another app to fix the issue.
          * <p>
-         * The extras bundle may contain {@link #EXTRA_PACKAGE_NAME} if one
-         * specific package was identified as the cause of the conflict. If
-         * unknown, or multiple packages, the extra may be {@code null}.
+         * The extras bundle may contain {@link #EXTRA_PACKAGE_NAME} with the
+         * specific packages identified as the cause of the conflict.
          */
         public static final int FAILURE_CONFLICT = 2;
 
@@ -626,6 +638,15 @@
          */
         public static final int FAILURE_INCOMPATIBLE = 4;
 
+        /**
+         * This install session failed because it was rejected. For example, the
+         * user declined requested permissions, or a package verifier rejected
+         * the session.
+         *
+         * @see PackageManager#VERIFICATION_REJECT
+         */
+        public static final int FAILURE_REJECTED = 5;
+
         public static final String EXTRA_PACKAGE_NAME = "android.content.pm.extra.PACKAGE_NAME";
 
         /**
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 1e4ed31..d5604cb 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -770,6 +770,9 @@
      */
     public static final int NO_NATIVE_LIBRARIES = -114;
 
+    /** {@hide} */
+    public static final int INSTALL_FAILED_REJECTED = -115;
+
     /**
      * Flag parameter for {@link #deletePackage} to indicate that you don't want to delete the
      * package's data directory.
@@ -3830,6 +3833,7 @@
             case INSTALL_FAILED_USER_RESTRICTED: return "INSTALL_FAILED_USER_RESTRICTED";
             case INSTALL_FAILED_DUPLICATE_PERMISSION: return "INSTALL_FAILED_DUPLICATE_PERMISSION";
             case INSTALL_FAILED_NO_MATCHING_ABIS: return "INSTALL_FAILED_NO_MATCHING_ABIS";
+            case INSTALL_FAILED_REJECTED: return "INSTALL_FAILED_REJECTED";
             default: return Integer.toString(status);
         }
     }
@@ -3857,8 +3861,8 @@
             case INSTALL_FAILED_CONTAINER_ERROR: return CommitCallback.FAILURE_STORAGE;
             case INSTALL_FAILED_INVALID_INSTALL_LOCATION: return CommitCallback.FAILURE_STORAGE;
             case INSTALL_FAILED_MEDIA_UNAVAILABLE: return CommitCallback.FAILURE_STORAGE;
-            case INSTALL_FAILED_VERIFICATION_TIMEOUT: return CommitCallback.FAILURE_UNKNOWN;
-            case INSTALL_FAILED_VERIFICATION_FAILURE: return CommitCallback.FAILURE_UNKNOWN;
+            case INSTALL_FAILED_VERIFICATION_TIMEOUT: return CommitCallback.FAILURE_REJECTED;
+            case INSTALL_FAILED_VERIFICATION_FAILURE: return CommitCallback.FAILURE_REJECTED;
             case INSTALL_FAILED_PACKAGE_CHANGED: return CommitCallback.FAILURE_INVALID;
             case INSTALL_FAILED_UID_CHANGED: return CommitCallback.FAILURE_INVALID;
             case INSTALL_FAILED_VERSION_DOWNGRADE: return CommitCallback.FAILURE_INVALID;
@@ -3876,6 +3880,7 @@
             case INSTALL_FAILED_USER_RESTRICTED: return CommitCallback.FAILURE_INCOMPATIBLE;
             case INSTALL_FAILED_DUPLICATE_PERMISSION: return CommitCallback.FAILURE_CONFLICT;
             case INSTALL_FAILED_NO_MATCHING_ABIS: return CommitCallback.FAILURE_INCOMPATIBLE;
+            case INSTALL_FAILED_REJECTED: return CommitCallback.FAILURE_REJECTED;
             default: return CommitCallback.FAILURE_UNKNOWN;
         }
     }
diff --git a/core/java/android/hardware/camera2/legacy/RequestThreadManager.java b/core/java/android/hardware/camera2/legacy/RequestThreadManager.java
index 2533a28..ec233da7 100644
--- a/core/java/android/hardware/camera2/legacy/RequestThreadManager.java
+++ b/core/java/android/hardware/camera2/legacy/RequestThreadManager.java
@@ -607,7 +607,6 @@
                             Log.e(TAG, "Interrupted while waiting for requests to complete.");
                         }
                         mDeviceState.setIdle();
-                        stopPreview();
                         break;
                     } else {
                         // Queue another capture if we did not get the last burst.
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 04e6227..3087506 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -864,15 +864,6 @@
     }
 
     /**
-     * Kept during L development to simplify updating unbundled apps.
-     * TODO: Remove after 2014-08-04
-     * @hide
-     */
-    public String getBadgedLabelForUser(String label, UserHandle user) {
-        return (String) getBadgedLabelForUser((CharSequence) label, user);
-    }
-
-    /**
      * If the target user is a managed profile of the calling user or the caller
      * is itself a managed profile, then this returns a drawable to use as a small
      * icon to include in a view to distinguish it from the original icon.
diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java
index 910f862..a410aa9 100644
--- a/core/java/android/view/GLES20Canvas.java
+++ b/core/java/android/view/GLES20Canvas.java
@@ -386,9 +386,6 @@
 
     @Override
     public void scale(float sx, float sy) {
-        // TODO: remove
-        if (sx > 1000000 || sy > 1000000) throw new IllegalArgumentException("invalid scales passed " + sx + ", " + sy);
-
         nScale(mRenderer, sx, sy);
     }
 
diff --git a/core/java/android/view/RenderNode.java b/core/java/android/view/RenderNode.java
index eee4973..099f153 100644
--- a/core/java/android/view/RenderNode.java
+++ b/core/java/android/view/RenderNode.java
@@ -598,9 +598,6 @@
      * @see #getScaleX()
      */
     public boolean setScaleX(float scaleX) {
-        if (scaleX > 1000000) {
-            throw new IllegalArgumentException("Invalid scale: " + scaleX);
-        }
         return nSetScaleX(mNativeRenderNode, scaleX);
     }
 
@@ -622,9 +619,6 @@
      * @see #getScaleY()
      */
     public boolean setScaleY(float scaleY) {
-        if (scaleY > 1000000) {
-            throw new IllegalArgumentException("Invalid scale: " + scaleY);
-        }
         return nSetScaleY(mNativeRenderNode, scaleY);
     }
 
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 129476c..60ef693 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -3996,6 +3996,22 @@
         return super.onGenericMotionEvent(event);
     }
 
+    /**
+     * Initiate a fling with the given velocity.
+     *
+     * <p>Applications can use this method to manually initiate a fling as if the user
+     * initiated it via touch interaction.</p>
+     *
+     * @param velocityY Vertical velocity in pixels per second
+     */
+    public void fling(int velocityY) {
+        if (mFlingRunnable == null) {
+            mFlingRunnable = new FlingRunnable();
+        }
+        reportScrollStateChange(OnScrollListener.SCROLL_STATE_FLING);
+        mFlingRunnable.start(-velocityY);
+    }
+
     @Override
     public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) {
         return ((nestedScrollAxes & SCROLL_AXIS_VERTICAL) != 0);
diff --git a/core/res/res/drawable-hdpi/cab_background_bottom_mtrl_alpha.9.png b/core/res/res/drawable-hdpi/cab_background_bottom_mtrl_alpha.9.png
new file mode 100644
index 0000000..92613b7
--- /dev/null
+++ b/core/res/res/drawable-hdpi/cab_background_bottom_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/cab_background_top_mtrl_alpha.9.png b/core/res/res/drawable-hdpi/cab_background_top_mtrl_alpha.9.png
new file mode 100644
index 0000000..e51ef28
--- /dev/null
+++ b/core/res/res/drawable-hdpi/cab_background_top_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/cab_background_bottom_mtrl_alpha.9.png b/core/res/res/drawable-mdpi/cab_background_bottom_mtrl_alpha.9.png
new file mode 100644
index 0000000..df292a0
--- /dev/null
+++ b/core/res/res/drawable-mdpi/cab_background_bottom_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/cab_background_top_mtrl_alpha.9.png b/core/res/res/drawable-mdpi/cab_background_top_mtrl_alpha.9.png
new file mode 100644
index 0000000..ae8cccd
--- /dev/null
+++ b/core/res/res/drawable-mdpi/cab_background_top_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/cab_background_bottom_mtrl_alpha.9.png b/core/res/res/drawable-xhdpi/cab_background_bottom_mtrl_alpha.9.png
new file mode 100644
index 0000000..9a4abd0
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/cab_background_bottom_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/cab_background_top_mtrl_alpha.9.png b/core/res/res/drawable-xhdpi/cab_background_top_mtrl_alpha.9.png
new file mode 100644
index 0000000..ed8d341
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/cab_background_top_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/cab_background_bottom_mtrl_alpha.9.png b/core/res/res/drawable-xxhdpi/cab_background_bottom_mtrl_alpha.9.png
new file mode 100644
index 0000000..22bd8ce
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/cab_background_bottom_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/cab_background_top_mtrl_alpha.9.png b/core/res/res/drawable-xxhdpi/cab_background_top_mtrl_alpha.9.png
new file mode 100644
index 0000000..1dd64b9
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/cab_background_top_mtrl_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable/cab_background_bottom_material.xml b/core/res/res/drawable/cab_background_bottom_material.xml
new file mode 100644
index 0000000..cfbc854
--- /dev/null
+++ b/core/res/res/drawable/cab_background_bottom_material.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android"
+    android:paddingMode="stack">
+    <item>
+        <shape android:shape="rectangle">
+            <solid android:color="?attr/colorBackground" />
+        </shape>
+    </item>
+    <item>
+        <nine-patch
+            android:src="@drawable/cab_background_bottom_mtrl_alpha"
+            android:tint="?attr/colorControlActivated" />
+    </item>
+</layer-list>
diff --git a/core/res/res/drawable/cab_background_top_material.xml b/core/res/res/drawable/cab_background_top_material.xml
new file mode 100644
index 0000000..09f7a61
--- /dev/null
+++ b/core/res/res/drawable/cab_background_top_material.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 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.
+-->
+
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android"
+    android:paddingMode="stack">
+    <item>
+        <shape android:shape="rectangle">
+            <solid android:color="?attr/colorBackground" />
+        </shape>
+    </item>
+    <item>
+        <nine-patch
+            android:src="@drawable/cab_background_top_mtrl_alpha"
+            android:tint="?attr/colorControlActivated" />
+    </item>
+</layer-list>
diff --git a/core/res/res/drawable/ic_corp_icon_badge.xml b/core/res/res/drawable/ic_corp_icon_badge.xml
index d20c431..834ae68 100644
--- a/core/res/res/drawable/ic_corp_icon_badge.xml
+++ b/core/res/res/drawable/ic_corp_icon_badge.xml
@@ -20,13 +20,11 @@
         android:viewportHeight="64.0">
 
     <path
-        android:fillColor="#FF000000"
-        android:pathData="M49.062,50.0m-14.0,0.0a14.0,14.0 0.0,1.0 1.0,28.0 0.0a14.0,14.0 0.0,1.0 1.0,-28.0 0.0"
-        android:fillOpacity="0.2"/>
+        android:fillColor="#33000000"
+        android:pathData="M49.062,50.0m-14.0,0.0a14.0,14.0 0.0,1.0 1.0,28.0 0.0a14.0,14.0 0.0,1.0 1.0,-28.0 0.0"/>
     <path
-        android:fillColor="#FF000000"
-        android:pathData="M49.0,49.5m-14.0,0.0a14.0,14.0 0.0,1.0 1.0,28.0 0.0a14.0,14.0 0.0,1.0 1.0,-28.0 0.0"
-        android:fillOpacity="0.2"/>
+        android:fillColor="#33000000"
+        android:pathData="M49.0,49.5m-14.0,0.0a14.0,14.0 0.0,1.0 1.0,28.0 0.0a14.0,14.0 0.0,1.0 1.0,-28.0 0.0"/>
     <path
         android:pathData="M49.0,49.0m-14.0,0.0a14.0,14.0 0.0,1.0 1.0,28.0 0.0a14.0,14.0 0.0,1.0 1.0,-28.0 0.0"
         android:fillColor="#FF5722"/>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 771690e..736f1a9d2 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -5208,13 +5208,13 @@
         <attr name="name" />
         <!-- The width a path stroke -->
         <attr name="strokeWidth" format="float" />
-        <!-- The opacity of a path stroke -->
+        <!-- The opacity of a path stroke @hide-->
         <attr name="strokeOpacity" format="float" />
         <!-- The color to stroke the path if not defined implies no stroke-->
         <attr name="strokeColor" format="color" />
         <!-- The color to fill the path if not defined implies no fill-->
         <attr name="fillColor" format="color" />
-        <!-- The level of opacity of the filled area of the path -->
+        <!-- The level of opacity of the filled area of the path @hide-->
         <attr name="fillOpacity" format="float" />
         <!-- The specification of the operations that define the path  -->
         <attr name="pathData" format="string" />
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 4bcf80d..f107a4c 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -5069,5 +5069,5 @@
     <!-- TV content rating system strings for ZA TV -->
 
     <!-- [CHAR_LIMIT=NONE] Battery saver: Feature description -->
-    <string name="battery_saver_description">To help improve battery life, battery saver will reduce your device’s performance and restrict background data.  Email, messaging, and other apps that rely on syncing may not update unless you open them.\n\nBattery saver turns off automatically when your device is charging.</string>
+    <string name="battery_saver_description">To help improve battery life, battery saver reduces your device’s performance and limits vibration and most background data. Email, messaging, and other apps that rely on syncing may not update unless you open them.\n\nBattery saver turns off automatically when your device is charging.</string>
 </resources>
diff --git a/core/res/res/values/themes_material.xml b/core/res/res/values/themes_material.xml
index e7001c3..6ee9a03 100644
--- a/core/res/res/values/themes_material.xml
+++ b/core/res/res/values/themes_material.xml
@@ -304,8 +304,8 @@
         <item name="actionButtonStyle">@style/Widget.Material.ActionButton</item>
         <item name="actionOverflowButtonStyle">@style/Widget.Material.ActionButton.Overflow</item>
         <item name="actionOverflowMenuStyle">@style/Widget.Material.PopupMenu.Overflow</item>
-        <item name="actionModeBackground">?attr/colorPrimaryDark</item>
-        <item name="actionModeSplitBackground">?attr/colorPrimaryDark</item>
+        <item name="actionModeBackground">@drawable/cab_background_top_material</item>
+        <item name="actionModeSplitBackground">@drawable/cab_background_bottom_material</item>
         <item name="actionModeCloseDrawable">@drawable/ic_cab_done_material</item>
         <item name="actionBarTabStyle">@style/Widget.Material.ActionBar.TabView</item>
         <item name="actionBarTabBarStyle">@style/Widget.Material.ActionBar.TabBar</item>
@@ -650,9 +650,8 @@
         <item name="actionButtonStyle">@style/Widget.Material.Light.ActionButton</item>
         <item name="actionOverflowButtonStyle">@style/Widget.Material.Light.ActionButton.Overflow</item>
         <item name="actionOverflowMenuStyle">@style/Widget.Material.Light.PopupMenu.Overflow</item>
-        <item name="actionBarPopupTheme">@null</item>
-        <item name="actionModeBackground">@drawable/cab_background_top_holo_light</item>
-        <item name="actionModeSplitBackground">@drawable/cab_background_bottom_holo_light</item>
+        <item name="actionModeBackground">@drawable/cab_background_top_material</item>
+        <item name="actionModeSplitBackground">@drawable/cab_background_bottom_material</item>
         <item name="actionModeCloseDrawable">@drawable/ic_cab_done_material</item>
         <item name="actionBarTabStyle">@style/Widget.Material.Light.ActionBar.TabView</item>
         <item name="actionBarTabBarStyle">@style/Widget.Material.Light.ActionBar.TabBar</item>
@@ -663,6 +662,7 @@
         <item name="actionBarSize">@dimen/action_bar_default_height_material</item>
         <item name="actionModePopupWindowStyle">@style/Widget.Material.Light.PopupWindow.ActionMode</item>
         <item name="actionBarWidgetTheme">@null</item>
+        <item name="actionBarPopupTheme">@null</item>
         <item name="actionBarTheme">@style/ThemeOverlay.Material.ActionBar</item>
         <item name="actionBarItemBackground">?attr/selectableItemBackgroundBorderless</item>
 
@@ -960,16 +960,8 @@
     </style>
 
     <!-- Theme for the search input bar. -->
-
-    <style name="Theme.Material.SearchBar" parent="Theme.Material.Panel">
-        <item name="actionModeBackground">@drawable/cab_background_top_holo_dark</item>
-        <item name="actionModeSplitBackground">@drawable/cab_background_bottom_holo_light</item>
-    </style>
-
-    <style name="Theme.Material.Light.SearchBar" parent="Theme.Material.Light.Panel">
-        <item name="actionModeBackground">@drawable/cab_background_top_holo_light</item>
-        <item name="actionModeSplitBackground">@drawable/cab_background_bottom_holo_light</item>
-    </style>
+    <style name="Theme.Material.SearchBar" parent="Theme.Material.Panel" />
+    <style name="Theme.Material.Light.SearchBar" parent="Theme.Material.Light.Panel" />
 
     <!-- Menu Themes -->
     <eat-comment />
diff --git a/data/fonts/fonts.xml b/data/fonts/fonts.xml
new file mode 100644
index 0000000..a4d4906
--- /dev/null
+++ b/data/fonts/fonts.xml
@@ -0,0 +1,233 @@
+<?xml version="1.0" encoding="utf-8"?>
+<familyset version="22">
+    <!-- first font is default -->
+    <family name="sans-serif">
+        <font weight="100" style="normal">Roboto-Thin.ttf</font>
+        <font weight="100" style="italic">Roboto-ThinItalic.ttf</font>
+        <font weight="300" style="normal">Roboto-Light.ttf</font>
+        <font weight="300" style="italic">Roboto-LightItalic.ttf</font>
+        <font weight="400" style="normal">Roboto-Regular.ttf</font>
+        <font weight="400" style="italic">Roboto-Italic.ttf</font>
+        <font weight="500" style="normal">Roboto-Medium.ttf</font>
+        <font weight="500" style="italic">Roboto-MediumItalic.ttf</font>
+        <font weight="700" style="normal">Roboto-Bold.ttf</font>
+        <font weight="700" style="italic">Roboto-BoldItalic.ttf</font>
+        <font weight="900" style="normal">Roboto-Black.ttf</font>
+        <font weight="900" style="italic">Roboto-BlackItalic.ttf</font>
+    </family>
+
+    <!-- Note that aliases must come after the fonts they reference. -->
+    <alias name="sans-serif-thin" to="sans-serif" weight="100" />
+    <alias name="sans-serif-light" to="sans-serif" weight="300" />
+    <alias name="sans-serif-black" to="sans-serif" weight="900" />
+    <alias name="arial" to="sans-serif" />
+    <alias name="helvetica" to="sans-serif" />
+    <alias name="tahoma" to="sans-serif" />
+    <alias name="verdana" to="sans-serif" />
+
+    <family name="sans-serif-condensed">
+        <font weight="300" style="normal">RobotoCondensed-Light.ttf</font>
+        <font weight="300" style="italic">RobotoCondensed-LightItalic.ttf</font>
+        <font weight="400" style="normal">RobotoCondensed-Regular.ttf</font>
+        <font weight="400" style="italic">RobotoCondensed-Italic.ttf</font>
+        <font weight="700" style="normal">RobotoCondensed-Bold.ttf</font>
+        <font weight="700" style="italic">RobotoCondensed-BoldItalic.ttf</font>
+    </family>
+    <alias name="sans-serif-condensed-light" to="sans-serif-condensed" weight="300" />
+
+    <family name="serif">
+        <font weight="400" style="normal">NotoSerif-Regular.ttf</font>
+        <font weight="700" style="normal">NotoSerif-Bold.ttf</font>
+        <font weight="400" style="italic">NotoSerif-Italic.ttf</font>
+        <font weight="700" style="italic">NotoSerif-BoldItalic.ttf</font>
+    </family>
+    <alias name="times" to="serif" />
+    <alias name="times new roman" to="serif" />
+    <alias name="palatino" to="serif" />
+    <alias name="georgia" to="serif" />
+    <alias name="baskerville" to="serif" />
+    <alias name="goudy" to="serif" />
+    <alias name="fantasy" to="serif" />
+    <alias name="ITC Stone Serif" to="serif" />
+
+    <family name="monospace">
+        <font weight="400" style="normal">DroidSansMono.ttf</font>
+    </family>
+    <alias name="courier" to="monospace" />
+    <alias name="courier new" to="monospace" />
+    <alias name="monaco" to="monospace" />
+
+    <family name="casual">
+        <font weight="400" style="normal">ComingSoon.ttf</font>
+    </family>
+
+    <family name="cursive">
+        <font weight="400" style="normal">DancingScript-Regular.ttf</font>
+        <font weight="700" style="normal">DancingScript-Bold.ttf</font>
+    </family>
+
+    <family name="sans-serif-smallcaps">
+        <font weight="400" style="normal">CarroisGothicSC-Regular.ttf</font>
+    </family>
+
+    <!-- fallback fonts -->
+    <family variant="elegant">
+        <font weight="400" style="normal">NotoNaskh-Regular.ttf</font>
+        <font weight="700" style="normal">NotoNaskh-Bold.ttf</font>
+    </family>
+    <family variant="compact">
+        <font weight="400" style="normal">NotoNaskhUI-Regular.ttf</font>
+        <font weight="700" style="normal">NotoNaskhUI-Bold.ttf</font>
+    </family>
+    <family>
+        <font weight="400" style="normal">NotoSansEthiopic-Regular.ttf</font>
+        <font weight="700" style="normal">NotoSansEthiopic-Bold.ttf</font>
+    </family>
+    <family>
+        <font weight="400" style="normal">NotoSansHebrew-Regular.ttf</font>
+        <font weight="700" style="normal">NotoSansHebrew-Bold.ttf</font>
+    </family>
+    <family variant="elegant">
+        <font weight="400" style="normal">NotoSansThai-Regular.ttf</font>
+        <font weight="700" style="normal">NotoSansThai-Bold.ttf</font>
+    </family>
+    <family variant="compact">
+        <font weight="400" style="normal">NotoSansThaiUI-Regular.ttf</font>
+        <font weight="700" style="normal">NotoSansThaiUI-Bold.ttf</font>
+    </family>
+    <family>
+        <font weight="400" style="normal">NotoSansArmenian-Regular.ttf</font>
+        <font weight="700" style="normal">NotoSansArmenian-Bold.ttf</font>
+    </family>
+    <family>
+        <font weight="400" style="normal">NotoSansGeorgian-Regular.ttf</font>
+        <font weight="700" style="normal">NotoSansGeorgian-Bold.ttf</font>
+    </family>
+    <family variant="elegant">
+        <font weight="400" style="normal">NotoSansDevanagari-Regular.ttf</font>
+        <font weight="700" style="normal">NotoSansDevanagari-Bold.ttf</font>
+    </family>
+    <family variant="compact">
+        <font weight="400" style="normal">NotoSansDevanagariUI-Regular.ttf</font>
+        <font weight="700" style="normal">NotoSansDevanagariUI-Bold.ttf</font>
+    </family>
+    <!-- Gujarati should come after Devanagari -->
+    <family variant="elegant">
+        <font weight="400" style="normal">NotoSansGujarati-Regular.ttf</font>
+        <font weight="700" style="normal">NotoSansGujarati-Bold.ttf</font>
+    </family>
+    <family variant="compact">
+        <font weight="400" style="normal">NotoSansGujaratiUI-Regular.ttf</font>
+        <font weight="700" style="normal">NotoSansGujaratiUI-Bold.ttf</font>
+    </family>
+    <!-- Gurmukhi should come after Devanagari -->
+    <family variant="elegant">
+        <font weight="400" style="normal">NotoSansGurmukhi-Regular.ttf</font>
+        <font weight="700" style="normal">NotoSansGurmukhi-Bold.ttf</font>
+    </family>
+    <family variant="compact">
+        <font weight="400" style="normal">NotoSansGurmukhiUI-Regular.ttf</font>
+        <font weight="700" style="normal">NotoSansGurmukhiUI-Bold.ttf</font>
+    </family>
+    <family variant="elegant">
+        <font weight="400" style="normal">NotoSansTamil-Regular.ttf</font>
+        <font weight="700" style="normal">NotoSansTamil-Bold.ttf</font>
+    </family>
+    <family variant="compact">
+        <font weight="400" style="normal">NotoSansTamilUI-Regular.ttf</font>
+        <font weight="700" style="normal">NotoSansTamilUI-Bold.ttf</font>
+    </family>
+    <family variant="elegant">
+        <font weight="400" style="normal">NotoSansMalayalam-Regular.ttf</font>
+        <font weight="700" style="normal">NotoSansMalayalam-Bold.ttf</font>
+    </family>
+    <family variant="compact">
+        <font weight="400" style="normal">NotoSansMalayalamUI-Regular.ttf</font>
+        <font weight="700" style="normal">NotoSansMalayalamUI-Bold.ttf</font>
+    </family>
+    <family variant="elegant">
+        <font weight="400" style="normal">NotoSansBengali-Regular.ttf</font>
+        <font weight="700" style="normal">NotoSansBengali-Bold.ttf</font>
+    </family>
+    <family variant="compact">
+        <font weight="400" style="normal">NotoSansBengaliUI-Regular.ttf</font>
+        <font weight="700" style="normal">NotoSansBengaliUI-Bold.ttf</font>
+    </family>
+    <family variant="elegant">
+        <font weight="400" style="normal">NotoSansTelugu-Regular.ttf</font>
+        <font weight="700" style="normal">NotoSansTelugu-Bold.ttf</font>
+    </family>
+    <family variant="compact">
+        <font weight="400" style="normal">NotoSansTeluguUI-Regular.ttf</font>
+        <font weight="700" style="normal">NotoSansTeluguUI-Bold.ttf</font>
+    </family>
+    <family variant="elegant">
+        <font weight="400" style="normal">NotoSansKannada-Regular.ttf</font>
+        <font weight="700" style="normal">NotoSansKannada-Bold.ttf</font>
+    </family>
+    <family variant="compact">
+        <font weight="400" style="normal">NotoSansKannadaUI-Regular.ttf</font>
+        <font weight="700" style="normal">NotoSansKannadaUI-Bold.ttf</font>
+    </family>
+    <family>
+        <font weight="400" style="normal">NotoSansSinhala-Regular.ttf</font>
+        <font weight="700" style="normal">NotoSansSinhala-Bold.ttf</font>
+    </family>
+    <family variant="elegant">
+        <font weight="400" style="normal">NotoSansKhmer-Regular.ttf</font>
+        <font weight="700" style="normal">NotoSansKhmer-Bold.ttf</font>
+    </family>
+    <family variant="compact">
+        <font weight="400" style="normal">NotoSansKhmerUI-Regular.ttf</font>
+        <font weight="700" style="normal">NotoSansKhmerUI-Bold.ttf</font>
+    </family>
+    <family variant="elegant">
+        <font weight="400" style="normal">NotoSansLao-Regular.ttf</font>
+        <font weight="700" style="normal">NotoSansLao-Bold.ttf</font>
+    </family>
+    <family variant="compact">
+        <font weight="400" style="normal">NotoSansLaoUI-Regular.ttf</font>
+        <font weight="700" style="normal">NotoSansLaoUI-Bold.ttf</font>
+    </family>
+    <family variant="elegant">
+        <font weight="400" style="normal">NotoSansMyanmar-Regular.ttf</font>
+        <font weight="700" style="normal">NotoSansMyanmar-Bold.ttf</font>
+    </family>
+    <family variant="compact">
+        <font weight="400" style="normal">NotoSansMyanmarUI-Regular.ttf</font>
+        <font weight="700" style="normal">NotoSansMyanmarUI-Bold.ttf</font>
+    </family>
+    <family>
+        <font weight="400" style="normal">NotoSansCherokee-Regular.ttf</font>
+    </family>
+    <family>
+        <font weight="400" style="normal">NotoSansCanadianAboriginal-Regular.ttf</font>
+    </family>
+    <family>
+        <font weight="400" style="normal">NotoSansYi-Regular.ttf</font>
+    </family>
+    <family lang="zh-Hans">
+        <font weight="400" style="normal">NotoSansHans-Regular.otf</font>
+    </family>
+    <family lang="zh-Hant">
+        <font weight="400" style="normal">NotoSansHant-Regular.otf</font>
+    </family>
+    <family lang="ja">
+        <font weight="400" style="normal">NotoSansJP-Regular.otf</font>
+    </family>
+    <family lang="ko">
+        <font weight="400" style="normal">NotoSansKR-Regular.otf</font>
+    </family>
+    <family>
+        <font weight="400" style="normal">NanumGothic.ttf</font>
+    </family>
+    <family>
+        <font weight="400" style="normal">NotoSansSymbols-Regular-Subsetted.ttf</font>
+    </family>
+    <family>
+        <font weight="400" style="normal">NotoColorEmoji.ttf</font>
+    </family>
+    <family lang="ja">
+        <font weight="400" style="normal">MTLmr3m.ttf</font>
+    </family>
+</familyset>
diff --git a/docs/html/tools/support-library/features.jd b/docs/html/tools/support-library/features.jd
index 65148bf..78946ee 100644
--- a/docs/html/tools/support-library/features.jd
+++ b/docs/html/tools/support-library/features.jd
@@ -225,8 +225,7 @@
 
 <p>This library provides {@link android.support.v7.media.MediaRouter}, {@link
 android.support.v7.media.MediaRouteProvider}, and related media classes that
-support the <a href="https://developers.google.com/cast/">Google Cast
-developer preview</a>. </p>
+support <a href="https://developers.google.com/cast/docs/android_sender">Google Cast</a>. </p>
 
 <p>In general, the APIs in the v7 mediarouter library provide a means of
 controlling the routing of media channels and streams from the current device to
@@ -258,9 +257,8 @@
 
 <p class="caution">The v7 mediarouter library APIs introduced in Support Library
 r18 are subject to change in later revisions of the Support Library. At this
-time, we recommend using the library only in connection with the <a
-href="https://developers.google.com/cast/">Google Cast
-developer preview</a>. </p>
+time, we recommend using the library only in connection with <a
+href="https://developers.google.com/cast/docs/android_sender">Google Cast</a>. </p>
 
 
 <h2 id="v8">v8 Support Library</h2>
diff --git a/graphics/java/android/graphics/drawable/VectorDrawable.java b/graphics/java/android/graphics/drawable/VectorDrawable.java
index 813797f..2b5823e 100644
--- a/graphics/java/android/graphics/drawable/VectorDrawable.java
+++ b/graphics/java/android/graphics/drawable/VectorDrawable.java
@@ -104,6 +104,8 @@
  * <dt><code>android:translateY</code></dt>
  * <dd>The amount of translation on the Y coordinate.
  * This is defined in the viewport space.</dd>
+ * <dt><code>android:alpha</code></dt>
+ * <dd>The amount of transparency.</dd>
  * </dl></dd>
  * </dl>
  *
@@ -117,15 +119,11 @@
  * <dd>Defines path string. This is using exactly same format as "d" attribute
  * in the SVG's path data. This is defined in the viewport space.</dd>
  * <dt><code>android:fillColor</code></dt>
- * <dd>Defines the color to fill the path (none if not present).</dd>
+ * <dd>Defines the color to fill the path (black if not present).</dd>
  * <dt><code>android:strokeColor</code></dt>
  * <dd>Defines the color to draw the path outline (none if not present).</dd>
  * <dt><code>android:strokeWidth</code></dt>
  * <dd>The width a path stroke.</dd>
- * <dt><code>android:strokeOpacity</code></dt>
- * <dd>The opacity of a path stroke.</dd>
- * <dt><code>android:fillOpacity</code></dt>
- * <dd>The opacity to fill the path with.</dd>
  * <dt><code>android:trimPathStart</code></dt>
  * <dd>The fraction of the path to trim from the start, in the range from 0 to 1.</dd>
  * <dt><code>android:trimPathEnd</code></dt>
@@ -1241,10 +1239,8 @@
 
         int mStrokeColor = 0;
         float mStrokeWidth = 0;
-        float mStrokeOpacity = Float.NaN;
         int mFillColor = Color.BLACK;
         int mFillRule;
-        float mFillOpacity = Float.NaN;
         float mTrimPathStart = 0;
         float mTrimPathEnd = 1;
         float mTrimPathOffset = 0;
@@ -1263,10 +1259,8 @@
 
             mStrokeColor = copy.mStrokeColor;
             mStrokeWidth = copy.mStrokeWidth;
-            mStrokeOpacity = copy.mStrokeOpacity;
             mFillColor = copy.mFillColor;
             mFillRule = copy.mFillRule;
-            mFillOpacity = copy.mFillOpacity;
             mTrimPathStart = copy.mTrimPathStart;
             mTrimPathEnd = copy.mTrimPathEnd;
             mTrimPathOffset = copy.mTrimPathOffset;
@@ -1327,8 +1321,6 @@
 
             mFillColor = a.getColor(R.styleable.VectorDrawablePath_fillColor,
                     mFillColor);
-            mFillOpacity = a.getFloat(R.styleable.VectorDrawablePath_fillOpacity,
-                    mFillOpacity);
             mStrokeLineCap = getStrokeLineCap(a.getInt(
                     R.styleable.VectorDrawablePath_strokeLineCap, -1), mStrokeLineCap);
             mStrokeLineJoin = getStrokeLineJoin(a.getInt(
@@ -1337,8 +1329,6 @@
                     R.styleable.VectorDrawablePath_strokeMiterLimit, mStrokeMiterlimit);
             mStrokeColor = a.getColor(R.styleable.VectorDrawablePath_strokeColor,
                     mStrokeColor);
-            mStrokeOpacity = a.getFloat(R.styleable.VectorDrawablePath_strokeOpacity,
-                    mStrokeOpacity);
             mStrokeWidth = a.getFloat(R.styleable.VectorDrawablePath_strokeWidth,
                     mStrokeWidth);
             mTrimPathEnd = a.getFloat(R.styleable.VectorDrawablePath_trimPathEnd,
@@ -1347,8 +1337,6 @@
                     R.styleable.VectorDrawablePath_trimPathOffset, mTrimPathOffset);
             mTrimPathStart = a.getFloat(
                     R.styleable.VectorDrawablePath_trimPathStart, mTrimPathStart);
-
-            updateColorAlphas();
         }
 
         @Override
@@ -1363,16 +1351,6 @@
             a.recycle();
         }
 
-        private void updateColorAlphas() {
-            if (!Float.isNaN(mFillOpacity)) {
-                mFillColor = applyAlpha(mFillColor, mFillOpacity);
-            }
-
-            if (!Float.isNaN(mStrokeOpacity)) {
-                mStrokeColor = applyAlpha(mStrokeColor, mStrokeOpacity);
-            }
-        }
-
         /* Setters and Getters, used by animator from AnimatedVectorDrawable. */
         @SuppressWarnings("unused")
         int getStroke() {
@@ -1395,16 +1373,6 @@
         }
 
         @SuppressWarnings("unused")
-        float getStrokeOpacity() {
-            return mStrokeOpacity;
-        }
-
-        @SuppressWarnings("unused")
-        void setStrokeOpacity(float strokeOpacity) {
-            mStrokeOpacity = strokeOpacity;
-        }
-
-        @SuppressWarnings("unused")
         int getFill() {
             return mFillColor;
         }
@@ -1415,16 +1383,6 @@
         }
 
         @SuppressWarnings("unused")
-        float getFillOpacity() {
-            return mFillOpacity;
-        }
-
-        @SuppressWarnings("unused")
-        void setFillOpacity(float fillOpacity) {
-            mFillOpacity = fillOpacity;
-        }
-
-        @SuppressWarnings("unused")
         float getTrimPathStart() {
             return mTrimPathStart;
         }
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index e00d2e3..396c7f3 100755
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -58,11 +58,6 @@
 // Defines
 ///////////////////////////////////////////////////////////////////////////////
 
-#define RAD_TO_DEG (180.0f / 3.14159265f)
-#define MIN_ANGLE 0.001f
-
-#define ALPHA_THRESHOLD 0
-
 static GLenum getFilter(const SkPaint* paint) {
     if (!paint || paint->getFilterLevel() != SkPaint::kNone_FilterLevel) {
         return GL_LINEAR;
@@ -692,7 +687,7 @@
             (fboLayer && clip.isEmpty())) {
         mSnapshot->empty = fboLayer;
     } else {
-        mSnapshot->invisible = mSnapshot->invisible || (alpha <= ALPHA_THRESHOLD && fboLayer);
+        mSnapshot->invisible = mSnapshot->invisible || (alpha <= 0 && fboLayer);
     }
 }
 
diff --git a/libs/hwui/PathTessellator.cpp b/libs/hwui/PathTessellator.cpp
index 209341c..e30ac19 100644
--- a/libs/hwui/PathTessellator.cpp
+++ b/libs/hwui/PathTessellator.cpp
@@ -49,6 +49,7 @@
 #include "Matrix.h"
 #include "Vector.h"
 #include "Vertex.h"
+#include "utils/MathUtils.h"
 
 namespace android {
 namespace uirenderer {
@@ -56,12 +57,11 @@
 #define OUTLINE_REFINE_THRESHOLD_SQUARED (0.5f * 0.5f)
 #define ROUND_CAP_THRESH 0.25f
 #define PI 3.1415926535897932f
+#define MAX_DEPTH 15
 
-// temporary error thresholds
-#define ERROR_DEPTH 20
-#define ERROR_SCALE 1e10
-#define ERROR_SQR_INV_THRESH 1e-20
-
+/**
+ * Extracts the x and y scale from the transform as positive values, and clamps them
+ */
 void PathTessellator::extractTessellationScales(const Matrix4& transform,
         float* scaleX, float* scaleY) {
     if (CC_LIKELY(transform.isPureTranslate())) {
@@ -72,11 +72,8 @@
         float m01 = transform.data[Matrix4::kSkewY];
         float m10 = transform.data[Matrix4::kSkewX];
         float m11 = transform.data[Matrix4::kScaleY];
-        *scaleX = sqrt(m00 * m00 + m01 * m01);
-        *scaleY = sqrt(m10 * m10 + m11 * m11);
-
-        LOG_ALWAYS_FATAL_IF(*scaleX > ERROR_SCALE || *scaleY > ERROR_SCALE,
-                "scales %e x %e too large for tessellation", *scaleX, *scaleY);
+        *scaleX = MathUtils::clampTessellationScale(sqrt(m00 * m00 + m01 * m01));
+        *scaleY = MathUtils::clampTessellationScale(sqrt(m10 * m10 + m11 * m11));
     }
 }
 
@@ -109,8 +106,8 @@
         } else {
             float scaleX, scaleY;
             PathTessellator::extractTessellationScales(transform, &scaleX, &scaleY);
-            inverseScaleX = (scaleX != 0) ? (1.0f / scaleX) : 1.0f;
-            inverseScaleY = (scaleY != 0) ? (1.0f / scaleY) : 1.0f;
+            inverseScaleX = 1.0f / scaleX;
+            inverseScaleY = 1.0f / scaleY;
         }
 
         if (isAA && halfStrokeWidth != 0 && inverseScaleX == inverseScaleY &&
@@ -914,9 +911,6 @@
         Vector<Vertex>& outputVertices) {
     ATRACE_CALL();
 
-    LOG_ALWAYS_FATAL_IF(sqrInvScaleX < ERROR_SQR_INV_THRESH || sqrInvScaleY < ERROR_SQR_INV_THRESH,
-            "Invalid scale factors used for approx %e, %e", sqrInvScaleX, sqrInvScaleY);
-
     // TODO: to support joins other than sharp miter, join vertices should be labelled in the
     // perimeter, or resolved into more vertices. Reconsider forceClose-ing in that case.
     SkPath::Iter iter(path, forceClose);
@@ -975,9 +969,6 @@
         float p2x, float p2y, float c2x, float c2y,
         float sqrInvScaleX, float sqrInvScaleY, float thresholdSquared,
         Vector<Vertex>& outputVertices, int depth) {
-    LOG_ALWAYS_FATAL_IF(depth >= ERROR_DEPTH, "ERROR DEPTH exceeded: cubic approx, invscale %e x %e, vertcount %d",
-            sqrInvScaleX, sqrInvScaleY, outputVertices.size());
-
     float dx = p2x - p1x;
     float dy = p2y - p1y;
     float d1 = fabs((c1x - p2x) * dy - (c1y - p2y) * dx);
@@ -985,7 +976,8 @@
     float d = d1 + d2;
 
     // multiplying by sqrInvScaleY/X equivalent to multiplying in dimensional scale factors
-    if (d * d < thresholdSquared * (dx * dx * sqrInvScaleY + dy * dy * sqrInvScaleX)) {
+    if (depth >= MAX_DEPTH
+            || d * d <= thresholdSquared * (dx * dx * sqrInvScaleY + dy * dy * sqrInvScaleX)) {
         // below thresh, draw line by adding endpoint
         pushToVector(outputVertices, p2x, p2y);
     } else {
@@ -1023,14 +1015,13 @@
         float cx, float cy,
         float sqrInvScaleX, float sqrInvScaleY, float thresholdSquared,
         Vector<Vertex>& outputVertices, int depth) {
-    LOG_ALWAYS_FATAL_IF(depth >= ERROR_DEPTH, "ERROR_DEPTH exceeded: quadratic approx, invscale %e x %e, vertcount %d",
-            sqrInvScaleX, sqrInvScaleY, outputVertices.size());
-
     float dx = bx - ax;
     float dy = by - ay;
     float d = (cx - bx) * dy - (cy - by) * dx;
 
-    if (d * d < thresholdSquared * (dx * dx * sqrInvScaleY + dy * dy * sqrInvScaleX)) {
+    // multiplying by sqrInvScaleY/X equivalent to multiplying in dimensional scale factors
+    if (depth >= MAX_DEPTH
+            || d * d <= thresholdSquared * (dx * dx * sqrInvScaleY + dy * dy * sqrInvScaleX)) {
         // below thresh, draw line by adding endpoint
         pushToVector(outputVertices, bx, by);
     } else {
diff --git a/libs/hwui/RenderProperties.h b/libs/hwui/RenderProperties.h
index 41f48cd..0c8d07f 100644
--- a/libs/hwui/RenderProperties.h
+++ b/libs/hwui/RenderProperties.h
@@ -313,7 +313,6 @@
     }
 
     bool setScaleX(float scaleX) {
-        LOG_ALWAYS_FATAL_IF(scaleX > 1000000, "invalid scaleX %e", scaleX);
         return RP_SET_AND_DIRTY(mPrimitiveFields.mScaleX, scaleX);
     }
 
@@ -322,7 +321,6 @@
     }
 
     bool setScaleY(float scaleY) {
-        LOG_ALWAYS_FATAL_IF(scaleY > 1000000, "invalid scaleY %e", scaleY);
         return RP_SET_AND_DIRTY(mPrimitiveFields.mScaleY, scaleY);
     }
 
diff --git a/libs/hwui/utils/MathUtils.h b/libs/hwui/utils/MathUtils.h
index 66bc127..6fb0411 100644
--- a/libs/hwui/utils/MathUtils.h
+++ b/libs/hwui/utils/MathUtils.h
@@ -35,6 +35,9 @@
         return value >= NON_ZERO_EPSILON;
     }
 
+    /**
+     * Clamps alpha value, and snaps when very near 0 or 1
+     */
     inline static float clampAlpha(float alpha) {
         if (alpha <= ALPHA_EPSILON) {
             return 0;
@@ -45,6 +48,20 @@
         }
     }
 
+    /*
+     * Clamps positive tessellation scale values
+     */
+    inline static float clampTessellationScale(float scale) {
+        const float MIN_SCALE = 0.0001;
+        const float MAX_SCALE = 1e10;
+        if (scale < MIN_SCALE) {
+            return MIN_SCALE;
+        } else if (scale > MAX_SCALE) {
+            return MAX_SCALE;
+        }
+        return scale;
+    }
+
     inline static bool areEqual(float valueA, float valueB) {
         return isZero(valueA - valueB);
     }
diff --git a/media/java/android/media/tv/ITvInputService.aidl b/media/java/android/media/tv/ITvInputService.aidl
index c98a48d..7a853d1 100644
--- a/media/java/android/media/tv/ITvInputService.aidl
+++ b/media/java/android/media/tv/ITvInputService.aidl
@@ -35,6 +35,6 @@
     // For hardware TvInputService
     void notifyHardwareAdded(in TvInputHardwareInfo hardwareInfo);
     void notifyHardwareRemoved(in TvInputHardwareInfo hardwareInfo);
-    void notifyHdmiCecDeviceAdded(in HdmiDeviceInfo deviceInfo);
-    void notifyHdmiCecDeviceRemoved(in HdmiDeviceInfo deviceInfo);
+    void notifyHdmiDeviceAdded(in HdmiDeviceInfo deviceInfo);
+    void notifyHdmiDeviceRemoved(in HdmiDeviceInfo deviceInfo);
 }
diff --git a/media/java/android/media/tv/ITvInputServiceCallback.aidl b/media/java/android/media/tv/ITvInputServiceCallback.aidl
index df648e7..de5d56f 100644
--- a/media/java/android/media/tv/ITvInputServiceCallback.aidl
+++ b/media/java/android/media/tv/ITvInputServiceCallback.aidl
@@ -24,7 +24,7 @@
  * @hide
  */
 oneway interface ITvInputServiceCallback {
-    void addHardwareTvInput(in int deviceID, in TvInputInfo inputInfo);
-    void addHdmiCecTvInput(in int logicalAddress, in TvInputInfo inputInfo);
+    void addHardwareTvInput(in int deviceId, in TvInputInfo inputInfo);
+    void addHdmiTvInput(in int logicalAddress, in TvInputInfo inputInfo);
     void removeTvInput(in String inputId);
 }
diff --git a/media/java/android/media/tv/TvContentRating.java b/media/java/android/media/tv/TvContentRating.java
index 2e4d031..18136e9 100644
--- a/media/java/android/media/tv/TvContentRating.java
+++ b/media/java/android/media/tv/TvContentRating.java
@@ -19,7 +19,6 @@
 import android.annotation.SystemApi;
 import android.net.Uri;
 import android.text.TextUtils;
-import android.util.Log;
 
 import java.util.Arrays;
 import java.util.Collections;
@@ -963,8 +962,6 @@
  * </table>
  */
 public final class TvContentRating {
-    private static final String TAG = "TvContentRating";
-
     /** @hide */
     public static final Uri SYSTEM_CONTENT_RATING_SYSTEM_XML = Uri.parse(
             "android.resource://system/" + com.android.internal.R.xml.tv_content_rating_systems);
diff --git a/media/java/android/media/tv/TvContract.java b/media/java/android/media/tv/TvContract.java
index a826957..ae6f5bc 100644
--- a/media/java/android/media/tv/TvContract.java
+++ b/media/java/android/media/tv/TvContract.java
@@ -20,7 +20,6 @@
 import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.ContentUris;
-import android.media.tv.TvContract.Programs;
 import android.net.Uri;
 import android.provider.BaseColumns;
 import android.util.ArraySet;
diff --git a/media/java/android/media/tv/TvInputInfo.java b/media/java/android/media/tv/TvInputInfo.java
index 8feb7e6..8d0e986 100644
--- a/media/java/android/media/tv/TvInputInfo.java
+++ b/media/java/android/media/tv/TvInputInfo.java
@@ -103,10 +103,10 @@
 
     private static final String XML_START_TAG_NAME = "tv-input";
     private static final String DELIMITER_INFO_IN_ID = "/";
-    private static final String PREFIX_CEC_DEVICE = "CEC";
+    private static final String PREFIX_HDMI_DEVICE = "HDMI";
     private static final String PREFIX_HARDWARE_DEVICE = "HW";
-    private static final int LENGTH_CEC_PHYSICAL_ADDRESS = 4;
-    private static final int LENGTH_CEC_LOGICAL_ADDRESS = 2;
+    private static final int LENGTH_HDMI_PHYSICAL_ADDRESS = 4;
+    private static final int LENGTH_HDMI_LOGICAL_ADDRESS = 2;
 
     private final ResolveInfo mService;
     private final String mId;
@@ -155,7 +155,7 @@
      * instantiating it from the given Context, ResolveInfo, and HdmiDeviceInfo.
      *
      * @param service The ResolveInfo returned from the package manager about this TV input service.
-     * @param cecInfo The HdmiDeviceInfo for a HDMI CEC logical device.
+     * @param deviceInfo The HdmiDeviceInfo for a HDMI CEC logical device.
      * @param parentId The ID of this TV input's parent input. {@code null} if none exists.
      * @param iconUri The {@link android.net.Uri} to load the icon image.
      *        {@see android.content.ContentResolver#openInputStream}. If it is null, the application
@@ -166,12 +166,12 @@
      */
     @SystemApi
     public static TvInputInfo createTvInputInfo(Context context, ResolveInfo service,
-            HdmiDeviceInfo cecInfo, String parentId, String label, Uri iconUri)
+            HdmiDeviceInfo deviceInfo, String parentId, String label, Uri iconUri)
                     throws XmlPullParserException, IOException {
-        boolean isConnectedToHdmiSwitch = (cecInfo.getPhysicalAddress() & 0x0FFF) != 0;
-        return createTvInputInfo(context, service, generateInputIdForHdmiCec(
+        boolean isConnectedToHdmiSwitch = (deviceInfo.getPhysicalAddress() & 0x0FFF) != 0;
+        return createTvInputInfo(context, service, generateInputIdForHdmiDevice(
                 new ComponentName(service.serviceInfo.packageName, service.serviceInfo.name),
-                cecInfo), parentId, TYPE_HDMI, label, iconUri, isConnectedToHdmiSwitch);
+                deviceInfo), parentId, TYPE_HDMI, label, iconUri, isConnectedToHdmiSwitch);
     }
 
     /**
@@ -497,16 +497,16 @@
      * Used to generate an input id from a ComponentName and HdmiDeviceInfo.
      *
      * @param name the component name for generating an input id.
-     * @param cecInfo HdmiDeviceInfo describing this TV input.
-     * @return the generated input id for the given {@code name} and {@code cecInfo}.
+     * @param deviceInfo HdmiDeviceInfo describing this TV input.
+     * @return the generated input id for the given {@code name} and {@code deviceInfo}.
      */
-    private static final String generateInputIdForHdmiCec(
-            ComponentName name, HdmiDeviceInfo cecInfo) {
-        // Example of the format : "/CEC%04X%02X"
-        String format = String.format("%s%s%%0%sX%%0%sX", DELIMITER_INFO_IN_ID, PREFIX_CEC_DEVICE,
-                LENGTH_CEC_PHYSICAL_ADDRESS, LENGTH_CEC_LOGICAL_ADDRESS);
+    private static final String generateInputIdForHdmiDevice(
+            ComponentName name, HdmiDeviceInfo deviceInfo) {
+        // Example of the format : "/HDMI%04X%02X"
+        String format = String.format("%s%s%%0%sX%%0%sX", DELIMITER_INFO_IN_ID, PREFIX_HDMI_DEVICE,
+                LENGTH_HDMI_PHYSICAL_ADDRESS, LENGTH_HDMI_LOGICAL_ADDRESS);
         return name.flattenToShortString() + String.format(format,
-                cecInfo.getPhysicalAddress(), cecInfo.getLogicalAddress());
+                deviceInfo.getPhysicalAddress(), deviceInfo.getLogicalAddress());
     }
 
     /**
diff --git a/media/java/android/media/tv/TvInputService.java b/media/java/android/media/tv/TvInputService.java
index 6a41c61..408ee7b 100644
--- a/media/java/android/media/tv/TvInputService.java
+++ b/media/java/android/media/tv/TvInputService.java
@@ -28,7 +28,6 @@
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
-import android.os.Looper;
 import android.os.Message;
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
@@ -42,7 +41,6 @@
 import android.view.KeyEvent;
 import android.view.MotionEvent;
 import android.view.Surface;
-import android.view.SurfaceView;
 import android.view.View;
 import android.view.WindowManager;
 import android.view.accessibility.CaptioningManager;
@@ -92,7 +90,7 @@
      * Handler instance to handle request from TV Input Manager Service. Should be run in the main
      * looper to be synchronously run with {@code Session.mHandler}.
      */
-    private Handler mServiceHandler = new ServiceHandler();
+    private final Handler mServiceHandler = new ServiceHandler();
     private final RemoteCallbackList<ITvInputServiceCallback> mCallbacks =
             new RemoteCallbackList<ITvInputServiceCallback>();
 
@@ -142,15 +140,15 @@
             }
 
             @Override
-            public void notifyHdmiCecDeviceAdded(HdmiDeviceInfo cecDeviceInfo) {
-                mServiceHandler.obtainMessage(ServiceHandler.DO_ADD_HDMI_CEC_TV_INPUT,
-                        cecDeviceInfo).sendToTarget();
+            public void notifyHdmiDeviceAdded(HdmiDeviceInfo deviceInfo) {
+                mServiceHandler.obtainMessage(ServiceHandler.DO_ADD_HDMI_TV_INPUT,
+                        deviceInfo).sendToTarget();
             }
 
             @Override
-            public void notifyHdmiCecDeviceRemoved(HdmiDeviceInfo cecDeviceInfo) {
-                mServiceHandler.obtainMessage(ServiceHandler.DO_REMOVE_HDMI_CEC_TV_INPUT,
-                        cecDeviceInfo).sendToTarget();
+            public void notifyHdmiDeviceRemoved(HdmiDeviceInfo deviceInfo) {
+                mServiceHandler.obtainMessage(ServiceHandler.DO_REMOVE_HDMI_TV_INPUT,
+                        deviceInfo).sendToTarget();
             }
         };
     }
@@ -203,27 +201,27 @@
 
     /**
      * Returns a new {@link TvInputInfo} object if this service is responsible for
-     * {@code cecDeviceInfo}; otherwise, return {@code null}. Override to modify default behavior
-     * of ignoring all HDMI CEC logical input device.
+     * {@code deviceInfo}; otherwise, return {@code null}. Override to modify default behavior of
+     * ignoring all HDMI logical input device.
      *
-     * @param cecDeviceInfo {@link HdmiDeviceInfo} object just added.
+     * @param deviceInfo {@link HdmiDeviceInfo} object just added.
      * @hide
      */
     @SystemApi
-    public TvInputInfo onHdmiCecDeviceAdded(HdmiDeviceInfo cecDeviceInfo) {
+    public TvInputInfo onHdmiDeviceAdded(HdmiDeviceInfo deviceInfo) {
         return null;
     }
 
     /**
-     * Returns the input ID for {@code logicalAddress} if it is handled by this service;
-     * otherwise, return {@code null}. Override to modify default behavior of ignoring all HDMI CEC
-     * logical input device.
+     * Returns the input ID for {@code logicalAddress} if it is handled by this service; otherwise,
+     * return {@code null}. Override to modify default behavior of ignoring all HDMI logical input
+     * device.
      *
-     * @param cecDeviceInfo {@link HdmiDeviceInfo} object just removed.
+     * @param deviceInfo {@link HdmiDeviceInfo} object just removed.
      * @hide
      */
     @SystemApi
-    public String onHdmiCecDeviceRemoved(HdmiDeviceInfo cecDeviceInfo) {
+    public String onHdmiDeviceRemoved(HdmiDeviceInfo deviceInfo) {
         return null;
     }
 
@@ -1164,8 +1162,8 @@
         private static final int DO_NOTIFY_SESSION_CREATED = 2;
         private static final int DO_ADD_HARDWARE_TV_INPUT = 3;
         private static final int DO_REMOVE_HARDWARE_TV_INPUT = 4;
-        private static final int DO_ADD_HDMI_CEC_TV_INPUT = 5;
-        private static final int DO_REMOVE_HDMI_CEC_TV_INPUT = 6;
+        private static final int DO_ADD_HDMI_TV_INPUT = 5;
+        private static final int DO_REMOVE_HDMI_TV_INPUT = 6;
 
         private void broadcastAddHardwareTvInput(int deviceId, TvInputInfo inputInfo) {
             int n = mCallbacks.beginBroadcast();
@@ -1179,12 +1177,11 @@
             mCallbacks.finishBroadcast();
         }
 
-        private void broadcastAddHdmiCecTvInput(
-                int logicalAddress, TvInputInfo inputInfo) {
+        private void broadcastAddHdmiTvInput(int logicalAddress, TvInputInfo inputInfo) {
             int n = mCallbacks.beginBroadcast();
             for (int i = 0; i < n; ++i) {
                 try {
-                    mCallbacks.getBroadcastItem(i).addHdmiCecTvInput(logicalAddress, inputInfo);
+                    mCallbacks.getBroadcastItem(i).addHdmiTvInput(logicalAddress, inputInfo);
                 } catch (RemoteException e) {
                     Log.e(TAG, "Error while broadcasting.", e);
                 }
@@ -1286,17 +1283,17 @@
                     }
                     return;
                 }
-                case DO_ADD_HDMI_CEC_TV_INPUT: {
-                    HdmiDeviceInfo cecDeviceInfo = (HdmiDeviceInfo) msg.obj;
-                    TvInputInfo inputInfo = onHdmiCecDeviceAdded(cecDeviceInfo);
+                case DO_ADD_HDMI_TV_INPUT: {
+                    HdmiDeviceInfo deviceInfo = (HdmiDeviceInfo) msg.obj;
+                    TvInputInfo inputInfo = onHdmiDeviceAdded(deviceInfo);
                     if (inputInfo != null) {
-                        broadcastAddHdmiCecTvInput(cecDeviceInfo.getLogicalAddress(), inputInfo);
+                        broadcastAddHdmiTvInput(deviceInfo.getLogicalAddress(), inputInfo);
                     }
                     return;
                 }
-                case DO_REMOVE_HDMI_CEC_TV_INPUT: {
-                    HdmiDeviceInfo cecDeviceInfo = (HdmiDeviceInfo) msg.obj;
-                    String inputId = onHdmiCecDeviceRemoved(cecDeviceInfo);
+                case DO_REMOVE_HDMI_TV_INPUT: {
+                    HdmiDeviceInfo deviceInfo = (HdmiDeviceInfo) msg.obj;
+                    String inputId = onHdmiDeviceRemoved(deviceInfo);
                     if (inputId != null) {
                         broadcastRemoveTvInput(inputId);
                     }
diff --git a/packages/Keyguard/src/com/android/keyguard/AppearAnimationUtils.java b/packages/Keyguard/src/com/android/keyguard/AppearAnimationUtils.java
index 6bb1f2c..e664fb9 100644
--- a/packages/Keyguard/src/com/android/keyguard/AppearAnimationUtils.java
+++ b/packages/Keyguard/src/com/android/keyguard/AppearAnimationUtils.java
@@ -26,38 +26,70 @@
  */
 public class AppearAnimationUtils implements AppearAnimationCreator<View> {
 
-    public static final long APPEAR_DURATION = 220;
+    public static final long DEFAULT_APPEAR_DURATION = 220;
 
-    private final Interpolator mLinearOutSlowIn;
+    private final Interpolator mInterpolator;
     private final float mStartTranslation;
     private final AppearAnimationProperties mProperties = new AppearAnimationProperties();
     private final float mDelayScale;
+    private final long mDuration;
 
     public AppearAnimationUtils(Context ctx) {
-        this(ctx, 1.0f, 1.0f);
+        this(ctx, DEFAULT_APPEAR_DURATION,
+                ctx.getResources().getDimensionPixelSize(R.dimen.appear_y_translation_start),
+                1.0f,
+                AnimationUtils.loadInterpolator(ctx, android.R.interpolator.linear_out_slow_in));
     }
 
-    public AppearAnimationUtils(Context ctx, float delayScaleFactor,
-            float translationScaleFactor) {
-        mLinearOutSlowIn = AnimationUtils.loadInterpolator(
-                ctx, android.R.interpolator.linear_out_slow_in);
+    public AppearAnimationUtils(Context ctx, long duration, float translationScaleFactor,
+            float delayScaleFactor, Interpolator interpolator) {
+        mInterpolator = interpolator;
         mStartTranslation = ctx.getResources().getDimensionPixelOffset(
                 R.dimen.appear_y_translation_start) * translationScaleFactor;
         mDelayScale = delayScaleFactor;
+        mDuration = duration;
     }
 
     public void startAppearAnimation(View[][] objects, final Runnable finishListener) {
         startAppearAnimation(objects, finishListener, this);
     }
 
+    public void startAppearAnimation(View[] objects, final Runnable finishListener) {
+        startAppearAnimation(objects, finishListener, this);
+    }
+
     public <T> void startAppearAnimation(T[][] objects, final Runnable finishListener,
             AppearAnimationCreator<T> creator) {
         AppearAnimationProperties properties = getDelays(objects);
         startAnimations(properties, objects, finishListener, creator);
     }
 
+    public <T> void startAppearAnimation(T[] objects, final Runnable finishListener,
+            AppearAnimationCreator<T> creator) {
+        AppearAnimationProperties properties = getDelays(objects);
+        startAnimations(properties, objects, finishListener, creator);
+    }
+
+    private <T> void startAnimations(AppearAnimationProperties properties, T[] objects,
+            final Runnable finishListener, AppearAnimationCreator<T> creator) {
+        if (properties.maxDelayRowIndex == -1 || properties.maxDelayColIndex == -1) {
+            finishListener.run();
+            return;
+        }
+        for (int row = 0; row < properties.delays.length; row++) {
+            long[] columns = properties.delays[row];
+            long delay = columns[0];
+            Runnable endRunnable = null;
+            if (properties.maxDelayRowIndex == row && properties.maxDelayColIndex == 0) {
+                endRunnable = finishListener;
+            }
+            creator.createAnimation(objects[row], delay, mDuration,
+                    mStartTranslation, mInterpolator, endRunnable);
+        }
+    }
+
     private <T> void startAnimations(AppearAnimationProperties properties, T[][] objects,
-            final Runnable finishListener, AppearAnimationCreator creator) {;
+            final Runnable finishListener, AppearAnimationCreator<T> creator) {
         if (properties.maxDelayRowIndex == -1 || properties.maxDelayColIndex == -1) {
             finishListener.run();
             return;
@@ -70,15 +102,32 @@
                 if (properties.maxDelayRowIndex == row && properties.maxDelayColIndex == col) {
                     endRunnable = finishListener;
                 }
-                creator.createAnimation(objects[row][col], delay, APPEAR_DURATION,
-                        mStartTranslation, mLinearOutSlowIn, endRunnable);
+                creator.createAnimation(objects[row][col], delay, mDuration,
+                        mStartTranslation, mInterpolator, endRunnable);
             }
         }
+    }
 
+    private <T> AppearAnimationProperties getDelays(T[] items) {
+        long maxDelay = -1;
+        mProperties.maxDelayColIndex = -1;
+        mProperties.maxDelayRowIndex = -1;
+        mProperties.delays = new long[items.length][];
+        for (int row = 0; row < items.length; row++) {
+            mProperties.delays[row] = new long[1];
+            long delay = calculateDelay(row, 0);
+            mProperties.delays[row][0] = delay;
+            if (items[row] != null && delay > maxDelay) {
+                maxDelay = delay;
+                mProperties.maxDelayColIndex = 0;
+                mProperties.maxDelayRowIndex = row;
+            }
+        }
+        return mProperties;
     }
 
     private <T> AppearAnimationProperties getDelays(T[][] items) {
-        long maxDelay = 0;
+        long maxDelay = -1;
         mProperties.maxDelayColIndex = -1;
         mProperties.maxDelayRowIndex = -1;
         mProperties.delays = new long[items.length][];
@@ -103,7 +152,7 @@
     }
 
     public Interpolator getInterpolator() {
-        return mLinearOutSlowIn;
+        return mInterpolator;
     }
 
     public float getStartTranslation() {
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java
index d5dcd71..12bbd35 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java
@@ -37,6 +37,7 @@
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.animation.AnimationUtils;
 import android.view.animation.Interpolator;
 import android.widget.Button;
 import android.widget.LinearLayout;
@@ -108,8 +109,10 @@
     public KeyguardPatternView(Context context, AttributeSet attrs) {
         super(context, attrs);
         mKeyguardUpdateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
-        mAppearAnimationUtils = new AppearAnimationUtils(context, 1.5f /* delayScale */,
-                2.0f /* transitionScale */);
+        mAppearAnimationUtils = new AppearAnimationUtils(context,
+                AppearAnimationUtils.DEFAULT_APPEAR_DURATION, 1.5f /* delayScale */,
+                2.0f /* transitionScale */, AnimationUtils.loadInterpolator(
+                        mContext, android.R.interpolator.linear_out_slow_in));
     }
 
     public void setKeyguardCallback(KeyguardSecurityCallback callback) {
@@ -420,7 +423,7 @@
                 this);
         if (!TextUtils.isEmpty(mHelpMessage.getText())) {
             mAppearAnimationUtils.createAnimation(mHelpMessage, 0,
-                    AppearAnimationUtils.APPEAR_DURATION,
+                    AppearAnimationUtils.DEFAULT_APPEAR_DURATION,
                     mAppearAnimationUtils.getStartTranslation(),
                     mAppearAnimationUtils.getInterpolator(),
                     null /* finishRunnable */);
diff --git a/packages/SystemUI/res/layout/keyguard_user_switcher.xml b/packages/SystemUI/res/layout/keyguard_user_switcher.xml
index 5648065..7c918c2 100644
--- a/packages/SystemUI/res/layout/keyguard_user_switcher.xml
+++ b/packages/SystemUI/res/layout/keyguard_user_switcher.xml
@@ -14,7 +14,8 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License
   -->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<com.android.systemui.statusbar.AlphaOptimizedLinearLayout
+        xmlns:android="http://schemas.android.com/apk/res/android"
         android:id="@+id/keyguard_user_switcher"
         android:orientation="vertical"
         android:layout_height="wrap_content"
@@ -22,4 +23,4 @@
         android:gravity="end"
         android:visibility="gone"
         android:paddingTop="4dp">
-</LinearLayout>
+</com.android.systemui.statusbar.AlphaOptimizedLinearLayout>
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailItemView.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailItemView.java
index 759d540..a56b7a7 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailItemView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailItemView.java
@@ -116,4 +116,9 @@
         boolean activated = ArrayUtils.contains(getDrawableState(), android.R.attr.state_activated);
         mName.setTypeface(activated ? mActivatedTypeface : mRegularTypeface);
     }
+
+    @Override
+    public boolean hasOverlappingRendering() {
+        return false;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
index 98bdee0..2a782cc 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
@@ -738,12 +738,12 @@
         }
     }
 
-    private void startApplicationDetailsActivity(String packageName) {
+    private void startApplicationDetailsActivity(String packageName, int userId) {
         Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
                 Uri.fromParts("package", packageName, null));
         intent.setComponent(intent.resolveActivity(getContext().getPackageManager()));
         TaskStackBuilder.create(getContext())
-                .addNextIntentWithParentStack(intent).startActivities();
+                .addNextIntentWithParentStack(intent).startActivities(null, new UserHandle(userId));
     }
 
     public boolean onInterceptTouchEvent(MotionEvent ev) {
@@ -769,7 +769,7 @@
                     ViewHolder viewHolder = (ViewHolder) selectedView.getTag();
                     if (viewHolder != null) {
                         final TaskDescription ad = viewHolder.taskDescription;
-                        startApplicationDetailsActivity(ad.packageName);
+                        startApplicationDetailsActivity(ad.packageName, ad.userId);
                         show(false);
                     } else {
                         throw new IllegalStateException("Oops, no tag on view " + selectedView);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java b/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java
index a55c0f2..bb5fe54 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java
@@ -328,7 +328,7 @@
         TaskStackViewLayoutAlgorithm algo = tsv.getStackAlgorithm();
         Rect taskStackBounds = new Rect(mTaskStackBounds);
         taskStackBounds.bottom -= mSystemInsets.bottom;
-        tsv.computeRects(mWindowRect.width(), mWindowRect.height(), taskStackBounds);
+        tsv.computeRects(mWindowRect.width(), mWindowRect.height(), taskStackBounds, mTriggeredFromAltTab);
         tsv.getScroller().setStackScrollToInitialState();
 
         // Find the running task in the TaskStack
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
index 1a32b81..34e8860 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -26,6 +26,7 @@
 import android.graphics.Canvas;
 import android.graphics.Rect;
 import android.net.Uri;
+import android.os.UserHandle;
 import android.provider.Settings;
 import android.util.AttributeSet;
 import android.view.LayoutInflater;
@@ -385,7 +386,7 @@
             sourceView = stackView;
             transform = stackView.getStackAlgorithm().getStackTransform(task, stackScroll, transform, null);
             offsetX = transform.rect.left;
-            offsetY = Math.min(transform.rect.top, mConfig.displayRect.height());
+            offsetY = mConfig.displayRect.height();
         } else {
             transform = stackView.getStackAlgorithm().getStackTransform(task, stackScroll, transform, null);
         }
@@ -474,7 +475,8 @@
                 Uri.fromParts("package", baseIntent.getComponent().getPackageName(), null));
         intent.setComponent(intent.resolveActivity(getContext().getPackageManager()));
         TaskStackBuilder.create(getContext())
-                .addNextIntentWithParentStack(intent).startActivities();
+                .addNextIntentWithParentStack(intent).startActivities(null,
+                new UserHandle(t.userId));
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
index 9e7dbf4..46996bb 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -349,9 +349,9 @@
     }
 
     /** Updates the min and max virtual scroll bounds */
-    void updateMinMaxScroll(boolean boundScrollToNewMinMax) {
+    void updateMinMaxScroll(boolean boundScrollToNewMinMax, boolean launchedWithAltTab) {
         // Compute the min and max scroll values
-        mLayoutAlgorithm.computeMinMaxScroll(mStack.getTasks());
+        mLayoutAlgorithm.computeMinMaxScroll(mStack.getTasks(), launchedWithAltTab);
 
         // Debug logging
         if (boundScrollToNewMinMax) {
@@ -388,17 +388,17 @@
                 };
             }
 
+            // Scroll the view into position (just center it in the curve)
             if (scrollToNewPosition) {
-                // Scroll the view into position
-                // XXX: We probably want this to be centered in view instead of p = 0f
-                float newScroll = mStackScroller.getBoundedStackScroll(
-                        mLayoutAlgorithm.getStackScrollForTaskIndex(t));
+                float newScroll = mLayoutAlgorithm.getStackScrollForTaskIndex(t) - 0.5f;
+                newScroll = mStackScroller.getBoundedStackScroll(newScroll);
                 mStackScroller.animateScroll(mStackScroller.getStackScroll(), newScroll, postScrollRunnable);
             } else {
                 if (postScrollRunnable != null) {
                     postScrollRunnable.run();
                 }
             }
+
         }
     }
 
@@ -435,12 +435,13 @@
     }
 
     /** Computes the stack and task rects */
-    public void computeRects(int windowWidth, int windowHeight, Rect taskStackBounds) {
+    public void computeRects(int windowWidth, int windowHeight, Rect taskStackBounds,
+                             boolean launchedWithAltTab) {
         // Compute the rects in the stack algorithm
         mLayoutAlgorithm.computeRects(windowWidth, windowHeight, taskStackBounds);
 
         // Update the scroll bounds
-        updateMinMaxScroll(false);
+        updateMinMaxScroll(false, launchedWithAltTab);
     }
 
     /**
@@ -455,7 +456,7 @@
         // Compute our stack/task rects
         Rect taskStackBounds = new Rect(mTaskStackBounds);
         taskStackBounds.bottom -= mConfig.systemInsets.bottom;
-        computeRects(width, height, taskStackBounds);
+        computeRects(width, height, taskStackBounds, mConfig.launchedWithAltTab);
 
         // If this is the first layout, then scroll to the front of the stack and synchronize the
         // stack views immediately to load all the views
@@ -543,9 +544,8 @@
             mStartEnterAnimationContext = null;
         }
 
-        // Update the focused task index to be the next item to the top task
+        // When Alt-Tabbing, we scroll to and focus the previous task
         if (mConfig.launchedWithAltTab) {
-            // When alt-tabbing, we focus the next previous task
             focusTask(Math.max(0, mStack.getTaskCount() - 2), false);
         }
     }
@@ -661,7 +661,7 @@
         mCb.onTaskViewDismissed(removedTask);
 
         // Update the min/max scroll and animate other task views into their new positions
-        updateMinMaxScroll(true);
+        updateMinMaxScroll(true, mConfig.launchedWithAltTab);
         requestSynchronizeStackViewsWithModel(200);
 
         // Update the new front most task
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java
index b1482bb..633e6fa 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java
@@ -18,7 +18,6 @@
 
 import android.graphics.Rect;
 import com.android.systemui.recents.RecentsConfiguration;
-import com.android.systemui.recents.misc.Console;
 import com.android.systemui.recents.misc.Utilities;
 import com.android.systemui.recents.model.Task;
 
@@ -87,8 +86,9 @@
                 left + size, mStackRect.top + size);
     }
 
-    /** Computes the minimum and maximum scroll progress values */
-    void computeMinMaxScroll(ArrayList<Task> tasks) {
+    /** Computes the minimum and maximum scroll progress values.  This method may be called before
+     * the RecentsConfiguration is set, so we need to pass in the alt-tab state. */
+    void computeMinMaxScroll(ArrayList<Task> tasks, boolean launchedWithAltTab) {
         // Clear the progress map
         mTaskProgressMap.clear();
 
@@ -130,7 +130,12 @@
 
         mMinScrollP = 0f;
         mMaxScrollP = pAtFrontMostCardTop - ((1f - pTaskHeightOffset - pNavBarOffset));
-        mInitialScrollP = pAtSecondFrontMostCardTop - ((1f - pTaskHeightOffset - pNavBarOffset));
+        if (launchedWithAltTab) {
+            // Center the second most task, since that will be focused first
+            mInitialScrollP = pAtSecondFrontMostCardTop - 0.5f;
+        } else {
+            mInitialScrollP = pAtSecondFrontMostCardTop - ((1f - pTaskHeightOffset - pNavBarOffset));
+        }
     }
 
     /** Update/get the transform */
@@ -151,6 +156,7 @@
         // If the task top is outside of the bounds below the screen, then immediately reset it
         if (pTaskRelative > 1f) {
             transformOut.reset();
+            transformOut.rect.set(mTaskRect);
             return transformOut;
         }
         // The check for the top is trickier, since we want to show the next task if it is at all
@@ -158,6 +164,7 @@
         if (pTaskRelative < 0f) {
             if (prevTransform != null && Float.compare(prevTransform.p, 0f) <= 0) {
                 transformOut.reset();
+                transformOut.rect.set(mTaskRect);
                 return transformOut;
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
index bf66c41..b66c310 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
@@ -16,10 +16,14 @@
 
 package com.android.systemui.statusbar.phone;
 
+import android.animation.LayoutTransition;
 import android.content.Context;
 import android.graphics.drawable.Drawable;
 import android.util.AttributeSet;
 import android.view.View;
+import android.view.ViewTreeObserver;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
 import android.widget.ImageView;
 import android.widget.RelativeLayout;
 import android.widget.TextView;
@@ -48,6 +52,7 @@
     private KeyguardUserSwitcher mKeyguardUserSwitcher;
 
     private int mSystemIconsSwitcherHiddenExpandedMargin;
+    private Interpolator mFastOutSlowInInterpolator;
 
     public KeyguardStatusBarView(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -61,6 +66,8 @@
         mMultiUserAvatar = (ImageView) findViewById(R.id.multi_user_avatar);
         mBatteryLevel = (TextView) findViewById(R.id.battery_level);
         loadDimens();
+        mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(getContext(),
+                android.R.interpolator.fast_out_slow_in);
         updateUserSwitcher();
     }
 
@@ -70,7 +77,14 @@
     }
 
     private void updateVisibilities() {
-        mMultiUserSwitch.setVisibility(!mKeyguardUserSwitcherShowing ? VISIBLE : GONE);
+        if (mMultiUserSwitch.getParent() != this && !mKeyguardUserSwitcherShowing) {
+            if (mMultiUserSwitch.getParent() != null) {
+                getOverlay().remove(mMultiUserSwitch);
+            }
+            addView(mMultiUserSwitch, 0);
+        } else if (mMultiUserSwitch.getParent() == this && mKeyguardUserSwitcherShowing) {
+            removeView(mMultiUserSwitch);
+        }
         mBatteryLevel.setVisibility(mBatteryCharging ? View.VISIBLE : View.GONE);
     }
 
@@ -137,12 +151,71 @@
         updateUserSwitcher();
     }
 
-    public void setKeyguardUserSwitcherShowing(boolean showing) {
+    public void setKeyguardUserSwitcherShowing(boolean showing, boolean animate) {
         mKeyguardUserSwitcherShowing = showing;
+        if (animate) {
+            animateNextLayoutChange();
+        }
         updateVisibilities();
         updateSystemIconsLayoutParams();
     }
 
+    private void animateNextLayoutChange() {
+        final int systemIconsCurrentX = mSystemIconsSuperContainer.getLeft();
+        final boolean userSwitcherVisible = mMultiUserSwitch.getParent() == this;
+        getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
+            @Override
+            public boolean onPreDraw() {
+                getViewTreeObserver().removeOnPreDrawListener(this);
+                boolean userSwitcherHiding = userSwitcherVisible
+                        && mMultiUserSwitch.getParent() != KeyguardStatusBarView.this;
+                mSystemIconsSuperContainer.setX(systemIconsCurrentX);
+                mSystemIconsSuperContainer.animate()
+                        .translationX(0)
+                        .setDuration(400)
+                        .setStartDelay(userSwitcherHiding ? 300 : 0)
+                        .setInterpolator(mFastOutSlowInInterpolator)
+                        .start();
+                if (userSwitcherHiding) {
+                    getOverlay().add(mMultiUserSwitch);
+                    mMultiUserSwitch.animate()
+                            .alpha(0f)
+                            .setDuration(300)
+                            .setStartDelay(0)
+                            .setInterpolator(PhoneStatusBar.ALPHA_OUT)
+                            .withEndAction(new Runnable() {
+                                @Override
+                                public void run() {
+                                    mMultiUserSwitch.setAlpha(1f);
+                                    getOverlay().remove(mMultiUserSwitch);
+                                }
+                            })
+                            .start();
+
+                } else {
+                    mMultiUserSwitch.setAlpha(0f);
+                    mMultiUserSwitch.animate()
+                            .alpha(1f)
+                            .setDuration(300)
+                            .setStartDelay(200)
+                            .setInterpolator(PhoneStatusBar.ALPHA_IN);
+                }
+                return true;
+            }
+        });
+
+    }
+
+    @Override
+    public void setVisibility(int visibility) {
+        super.setVisibility(visibility);
+        if (visibility != View.VISIBLE) {
+            mSystemIconsSuperContainer.animate().cancel();
+            mMultiUserSwitch.animate().cancel();
+            mMultiUserSwitch.setAlpha(1f);
+        }
+    }
+
     @Override
     public boolean hasOverlappingRendering() {
         return false;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java
index af30266..47325c8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java
@@ -65,7 +65,7 @@
         if (um.isUserSwitcherEnabled()) {
             if (mKeyguardMode) {
                 if (mKeyguardUserSwitcher != null) {
-                    mKeyguardUserSwitcher.show();
+                    mKeyguardUserSwitcher.show(true /* animate */);
                 }
             } else {
                 mQsPanel.showDetailAdapter(true,
@@ -78,4 +78,9 @@
             getContext().startActivityAsUser(intent, new UserHandle(UserHandle.USER_CURRENT));
         }
     }
+
+    @Override
+    public boolean hasOverlappingRendering() {
+        return false;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index e70422b..74ae4a4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -950,7 +950,7 @@
                         ? View.VISIBLE
                         : View.INVISIBLE);
         if (mKeyguardUserSwitcher != null && mQsExpanded && !mStackScrollerOverscrolling) {
-            mKeyguardUserSwitcher.hide();
+            mKeyguardUserSwitcher.hide(true /* animate */);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 1d678af..75e31e4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -3341,7 +3341,7 @@
 
     public void showKeyguard() {
         setBarState(StatusBarState.KEYGUARD);
-        updateKeyguardState(false /* goingToFullShade */);
+        updateKeyguardState(false /* goingToFullShade */, false /* fromShadeLocked */);
         instantExpandNotificationsPanel();
         mLeaveOpenOnKeyguardHide = false;
         if (mDraggedDownRow != null) {
@@ -3413,7 +3413,7 @@
         } else {
             instantCollapseNotificationPanel();
         }
-        updateKeyguardState(staying);
+        updateKeyguardState(staying, false /* fromShadeLocked */);
         return staying;
     }
 
@@ -3449,14 +3449,15 @@
                 && mStatusBarKeyguardViewManager.isSecure());
     }
 
-    private void updateKeyguardState(boolean goingToFullShade) {
+    private void updateKeyguardState(boolean goingToFullShade, boolean fromShadeLocked) {
         if (mState == StatusBarState.KEYGUARD) {
             mKeyguardIndicationController.setVisible(true);
             mNotificationPanel.resetViews();
-            mKeyguardUserSwitcher.setKeyguard(true);
+            mKeyguardUserSwitcher.setKeyguard(true, fromShadeLocked);
         } else {
             mKeyguardIndicationController.setVisible(false);
-            mKeyguardUserSwitcher.setKeyguard(false);
+            mKeyguardUserSwitcher.setKeyguard(false,
+                    goingToFullShade || mState == StatusBarState.SHADE_LOCKED || fromShadeLocked);
         }
         if (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED) {
             mScrimController.setKeyguardShowing(true);
@@ -3686,7 +3687,7 @@
         } else {
             mNotificationPanel.animateToFullShade(0 /* delay */);
             setBarState(StatusBarState.SHADE_LOCKED);
-            updateKeyguardState(false /* goingToFullShade */);
+            updateKeyguardState(false /* goingToFullShade */, false /* fromShadeLocked */);
             if (row != null) {
                 row.setUserLocked(false);
             }
@@ -3699,7 +3700,7 @@
     public void goToKeyguard() {
         if (mState == StatusBarState.SHADE_LOCKED) {
             setBarState(StatusBarState.KEYGUARD);
-            updateKeyguardState(false /* goingToFullShade */);
+            updateKeyguardState(false /* goingToFullShade */, true /* fromShadeLocked*/);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java
index 203196e..18583ee 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java
@@ -16,18 +16,25 @@
 
 package com.android.systemui.statusbar.policy;
 
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
 import android.content.Context;
 import android.database.DataSetObserver;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewStub;
+import android.view.animation.AnimationUtils;
 import android.widget.TextView;
 
+import com.android.keyguard.AppearAnimationUtils;
 import com.android.systemui.R;
 import com.android.systemui.qs.tiles.UserDetailItemView;
 import com.android.systemui.statusbar.phone.KeyguardStatusBarView;
 import com.android.systemui.statusbar.phone.NotificationPanelView;
+import com.android.systemui.statusbar.phone.PhoneStatusBar;
 import com.android.systemui.statusbar.phone.StatusBarHeaderView;
 import com.android.systemui.statusbar.phone.UserAvatarView;
 
@@ -43,33 +50,42 @@
     private final KeyguardStatusBarView mStatusBarView;
     private final Adapter mAdapter;
     private final boolean mSimpleUserSwitcher;
+    private final AppearAnimationUtils mAppearAnimationUtils;
+    private final KeyguardUserSwitcherScrim mBackground;
+    private ObjectAnimator mBgAnimator;
 
     public KeyguardUserSwitcher(Context context, ViewStub userSwitcher,
             KeyguardStatusBarView statusBarView, NotificationPanelView panelView,
             UserSwitcherController userSwitcherController) {
         if (context.getResources().getBoolean(R.bool.config_keyguardUserSwitcher) || ALWAYS_ON) {
             mUserSwitcher = (ViewGroup) userSwitcher.inflate();
-            mUserSwitcher.setBackground(new KeyguardUserSwitcherScrim(mUserSwitcher));
+            mBackground = new KeyguardUserSwitcherScrim(mUserSwitcher);
+            mUserSwitcher.setBackground(mBackground);
             mStatusBarView = statusBarView;
             mStatusBarView.setKeyguardUserSwitcher(this);
             panelView.setKeyguardUserSwitcher(this);
             mAdapter = new Adapter(context, userSwitcherController);
             mAdapter.registerDataSetObserver(mDataSetObserver);
             mSimpleUserSwitcher = userSwitcherController.isSimpleUserSwitcher();
+            mAppearAnimationUtils = new AppearAnimationUtils(context, 400, -0.5f, 0.5f,
+                    AnimationUtils.loadInterpolator(
+                            context, android.R.interpolator.fast_out_slow_in));
         } else {
             mUserSwitcher = null;
             mStatusBarView = null;
             mAdapter = null;
             mSimpleUserSwitcher = false;
+            mAppearAnimationUtils = null;
+            mBackground = null;
         }
     }
 
-    public void setKeyguard(boolean keyguard) {
+    public void setKeyguard(boolean keyguard, boolean animate) {
         if (mUserSwitcher != null) {
             if (keyguard && shouldExpandByDefault()) {
-                show();
+                show(animate);
             } else {
-                hide();
+                hide(animate);
             }
         }
     }
@@ -82,22 +98,81 @@
         return mSimpleUserSwitcher;
     }
 
-    public void show() {
-        if (mUserSwitcher != null) {
-            // TODO: animate
+    public void show(boolean animate) {
+        if (mUserSwitcher != null && mUserSwitcher.getVisibility() != View.VISIBLE) {
+            cancelAnimations();
             mUserSwitcher.setVisibility(View.VISIBLE);
-            mStatusBarView.setKeyguardUserSwitcherShowing(true);
+            mStatusBarView.setKeyguardUserSwitcherShowing(true, animate);
+            if (animate) {
+                startAppearAnimation();
+            }
         }
     }
 
-    public void hide() {
+    public void hide(boolean animate) {
         if (mUserSwitcher != null && mUserSwitcher.getVisibility() == View.VISIBLE) {
-            // TODO: animate
-            mUserSwitcher.setVisibility(View.GONE);
-            mStatusBarView.setKeyguardUserSwitcherShowing(false);
+            cancelAnimations();
+            if (animate) {
+                startDisappearAnimation();
+            } else {
+                mUserSwitcher.setVisibility(View.GONE);
+            }
+            mStatusBarView.setKeyguardUserSwitcherShowing(false, animate);
         }
     }
 
+    private void cancelAnimations() {
+        int count = mUserSwitcher.getChildCount();
+        for (int i = 0; i < count; i++) {
+            mUserSwitcher.getChildAt(i).animate().cancel();
+        }
+        if (mBgAnimator != null) {
+            mBgAnimator.cancel();
+        }
+        mUserSwitcher.animate().cancel();
+    }
+
+    private void startAppearAnimation() {
+        int count = mUserSwitcher.getChildCount();
+        View[] objects = new View[count];
+        for (int i = 0; i < count; i++) {
+            objects[i] = mUserSwitcher.getChildAt(i);
+        }
+        mUserSwitcher.setClipChildren(false);
+        mUserSwitcher.setClipToPadding(false);
+        mAppearAnimationUtils.startAppearAnimation(objects, new Runnable() {
+            @Override
+            public void run() {
+                mUserSwitcher.setClipChildren(true);
+                mUserSwitcher.setClipToPadding(true);
+            }
+        });
+        mBgAnimator = ObjectAnimator.ofInt(mBackground, "alpha", 0, 255);
+        mBgAnimator.setDuration(400);
+        mBgAnimator.setInterpolator(PhoneStatusBar.ALPHA_IN);
+        mBgAnimator.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                mBgAnimator = null;
+            }
+        });
+        mBgAnimator.start();
+    }
+
+    private void startDisappearAnimation() {
+        mUserSwitcher.animate()
+                .alpha(0f)
+                .setDuration(300)
+                .setInterpolator(PhoneStatusBar.ALPHA_OUT)
+                .withEndAction(new Runnable() {
+                    @Override
+                    public void run() {
+                        mUserSwitcher.setVisibility(View.GONE);
+                        mUserSwitcher.setAlpha(1f);
+                    }
+                });
+    }
+
     private void refresh() {
         final int childCount = mUserSwitcher.getChildCount();
         final int adapterCount = mAdapter.getCount();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherScrim.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherScrim.java
index 3356afd..4363037 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherScrim.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherScrim.java
@@ -19,6 +19,7 @@
 import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.ColorFilter;
+import android.graphics.LightingColorFilter;
 import android.graphics.Paint;
 import android.graphics.PixelFormat;
 import android.graphics.RadialGradient;
@@ -41,7 +42,9 @@
 
     private int mDarkColor;
     private int mTop;
+    private int mAlpha;
     private Paint mRadialGradientPaint = new Paint();
+    private int mLayoutWidth;
 
     public KeyguardUserSwitcherScrim(View host) {
         host.addOnLayoutChangeListener(this);
@@ -56,13 +59,21 @@
         float width = bounds.width() * OUTER_EXTENT;
         float height = (mTop + bounds.height()) * OUTER_EXTENT;
         canvas.translate(0, -mTop);
-        canvas.scale(1, height/width);
+        canvas.scale(1, height / width);
         canvas.drawRect(isLtr ? bounds.right - width : 0, 0,
                 isLtr ? bounds.right : bounds.left + width, width, mRadialGradientPaint);
     }
 
     @Override
     public void setAlpha(int alpha) {
+        mAlpha = alpha;
+        updatePaint();
+        invalidateSelf();
+    }
+
+    @Override
+    public int getAlpha() {
+        return mAlpha;
     }
 
     @Override
@@ -78,15 +89,24 @@
     public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft,
             int oldTop, int oldRight, int oldBottom) {
         if (left != oldLeft || top != oldTop || right != oldRight || bottom != oldBottom) {
-            int width = right - left;
-            float radius = width * OUTER_EXTENT;
-            boolean isLtr = getLayoutDirection() == LayoutDirection.LTR;
-            mRadialGradientPaint.setShader(
-                    new RadialGradient(isLtr ? width : 0, 0, radius,
-                            new int[] { mDarkColor, Color.TRANSPARENT},
-                            new float[] { Math.max(0f, width * INNER_EXTENT / radius), 1f},
-                            Shader.TileMode.CLAMP));
+            mLayoutWidth = right - left;
             mTop = top;
+            updatePaint();
         }
     }
+
+    private void updatePaint() {
+        if (mLayoutWidth == 0) {
+            return;
+        }
+        float radius = mLayoutWidth * OUTER_EXTENT;
+        boolean isLtr = getLayoutDirection() == LayoutDirection.LTR;
+        mRadialGradientPaint.setShader(
+                new RadialGradient(isLtr ? mLayoutWidth : 0, 0, radius,
+                        new int[] { Color.argb(
+                                        (int) (Color.alpha(mDarkColor) * mAlpha / 255f), 0, 0, 0),
+                                Color.TRANSPARENT },
+                        new float[] { Math.max(0f, mLayoutWidth * INNER_EXTENT / radius), 1f },
+                        Shader.TileMode.CLAMP));
+    }
 }
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 6761f24..f9baccd 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -251,10 +251,7 @@
     private int mNetworkPreference;
     private int mActiveDefaultNetwork = -1;
     // 0 is full bad, 100 is full good
-    private int mDefaultInetCondition = 0;
     private int mDefaultInetConditionPublished = 0;
-    private boolean mInetConditionChangeInFlight = false;
-    private int mDefaultConnectionSequence = 0;
 
     private Object mDnsLock = new Object();
     private int mNumDnsEntries;
@@ -274,19 +271,6 @@
     private static final int EVENT_CHANGE_MOBILE_DATA_ENABLED = 2;
 
     /**
-     * used internally to synchronize inet condition reports
-     * arg1 = networkType
-     * arg2 = condition (0 bad, 100 good)
-     */
-    private static final int EVENT_INET_CONDITION_CHANGE = 4;
-
-    /**
-     * used internally to mark the end of inet condition hold periods
-     * arg1 = networkType
-     */
-    private static final int EVENT_INET_CONDITION_HOLD_END = 5;
-
-    /**
      * used internally to clear a wakelock when transitioning
      * from one net to another.  Clear happens when we get a new
      * network - EVENT_EXPIRE_NET_TRANSITION_WAKELOCK happens
@@ -490,10 +474,6 @@
             mTypeLists[type] = new ArrayList<NetworkAgentInfo>();
         }
 
-        private boolean isDefaultNetwork(NetworkAgentInfo nai) {
-            return mNetworkForRequestId.get(mDefaultRequest.requestId) == nai;
-        }
-
         public boolean isTypeSupported(int type) {
             return isNetworkTypeValid(type) && mTypeLists[type] != null;
         }
@@ -2052,6 +2032,9 @@
                 nai.networkInfo.setDetailedState(NetworkInfo.DetailedState.DISCONNECTED,
                         null, null);
             }
+            if (isDefaultNetwork(nai)) {
+                mDefaultInetConditionPublished = 0;
+            }
             notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_LOST);
             nai.networkMonitor.sendMessage(NetworkMonitor.CMD_NETWORK_DISCONNECTED);
             mNetworkAgentInfos.remove(msg.replyTo);
@@ -2222,18 +2205,6 @@
                     }
                     break;
                 }
-                case EVENT_INET_CONDITION_CHANGE: {
-                    int netType = msg.arg1;
-                    int condition = msg.arg2;
-                    handleInetConditionChange(netType, condition);
-                    break;
-                }
-                case EVENT_INET_CONDITION_HOLD_END: {
-                    int netType = msg.arg1;
-                    int sequence = msg.arg2;
-                    handleInetConditionHoldEnd(netType, sequence);
-                    break;
-                }
                 case EVENT_APPLY_GLOBAL_HTTP_PROXY: {
                     handleDeprecatedGlobalHttpProxy();
                     break;
@@ -2428,99 +2399,15 @@
 
     // 100 percent is full good, 0 is full bad.
     public void reportInetCondition(int networkType, int percentage) {
-        if (VDBG) log("reportNetworkCondition(" + networkType + ", " + percentage + ")");
-        mContext.enforceCallingOrSelfPermission(
-                android.Manifest.permission.STATUS_BAR,
-                "ConnectivityService");
-
-        if (DBG) {
-            int pid = getCallingPid();
-            int uid = getCallingUid();
-            String s = pid + "(" + uid + ") reports inet is " +
-                (percentage > 50 ? "connected" : "disconnected") + " (" + percentage + ") on " +
-                "network Type " + networkType + " at " + GregorianCalendar.getInstance().getTime();
-            mInetLog.add(s);
-            while(mInetLog.size() > INET_CONDITION_LOG_MAX_SIZE) {
-                mInetLog.remove(0);
-            }
-        }
-        mHandler.sendMessage(mHandler.obtainMessage(
-            EVENT_INET_CONDITION_CHANGE, networkType, percentage));
+        if (percentage > 50) return;  // don't handle good network reports
+        NetworkAgentInfo nai = mLegacyTypeTracker.getNetworkForType(networkType);
+        if (nai != null) reportBadNetwork(nai.network);
     }
 
     public void reportBadNetwork(Network network) {
         //TODO
     }
 
-    private void handleInetConditionChange(int netType, int condition) {
-        if (mActiveDefaultNetwork == -1) {
-            if (DBG) log("handleInetConditionChange: no active default network - ignore");
-            return;
-        }
-        if (mActiveDefaultNetwork != netType) {
-            if (DBG) log("handleInetConditionChange: net=" + netType +
-                            " != default=" + mActiveDefaultNetwork + " - ignore");
-            return;
-        }
-        if (VDBG) {
-            log("handleInetConditionChange: net=" +
-                    netType + ", condition=" + condition +
-                    ",mActiveDefaultNetwork=" + mActiveDefaultNetwork);
-        }
-        mDefaultInetCondition = condition;
-        int delay;
-        if (mInetConditionChangeInFlight == false) {
-            if (VDBG) log("handleInetConditionChange: starting a change hold");
-            // setup a new hold to debounce this
-            if (mDefaultInetCondition > 50) {
-                delay = Settings.Global.getInt(mContext.getContentResolver(),
-                        Settings.Global.INET_CONDITION_DEBOUNCE_UP_DELAY, 500);
-            } else {
-                delay = Settings.Global.getInt(mContext.getContentResolver(),
-                        Settings.Global.INET_CONDITION_DEBOUNCE_DOWN_DELAY, 3000);
-            }
-            mInetConditionChangeInFlight = true;
-            mHandler.sendMessageDelayed(mHandler.obtainMessage(EVENT_INET_CONDITION_HOLD_END,
-                    mActiveDefaultNetwork, mDefaultConnectionSequence), delay);
-        } else {
-            // we've set the new condition, when this hold ends that will get picked up
-            if (VDBG) log("handleInetConditionChange: currently in hold - not setting new end evt");
-        }
-    }
-
-    private void handleInetConditionHoldEnd(int netType, int sequence) {
-        if (DBG) {
-            log("handleInetConditionHoldEnd: net=" + netType +
-                    ", condition=" + mDefaultInetCondition +
-                    ", published condition=" + mDefaultInetConditionPublished);
-        }
-        mInetConditionChangeInFlight = false;
-
-        if (mActiveDefaultNetwork == -1) {
-            if (DBG) log("handleInetConditionHoldEnd: no active default network - ignoring");
-            return;
-        }
-        if (mDefaultConnectionSequence != sequence) {
-            if (DBG) log("handleInetConditionHoldEnd: event hold for obsolete network - ignoring");
-            return;
-        }
-        // TODO: Figure out why this optimization sometimes causes a
-        //       change in mDefaultInetCondition to be missed and the
-        //       UI to not be updated.
-        //if (mDefaultInetConditionPublished == mDefaultInetCondition) {
-        //    if (DBG) log("no change in condition - aborting");
-        //    return;
-        //}
-        NetworkInfo networkInfo = getNetworkInfoForType(mActiveDefaultNetwork);
-        if (networkInfo.isConnected() == false) {
-            if (DBG) log("handleInetConditionHoldEnd: default network not connected - ignoring");
-            return;
-        }
-        mDefaultInetConditionPublished = mDefaultInetCondition;
-        sendInetConditionBroadcast(networkInfo);
-        return;
-    }
-
     public ProxyInfo getProxy() {
         // this information is already available as a world read/writable jvm property
         // so this API change wouldn't have a benifit.  It also breaks the passing
@@ -4206,6 +4093,10 @@
 
     private final NetworkRequest mDefaultRequest;
 
+    private boolean isDefaultNetwork(NetworkAgentInfo nai) {
+        return mNetworkForRequestId.get(mDefaultRequest.requestId) == nai;
+    }
+
     public void registerNetworkAgent(Messenger messenger, NetworkInfo networkInfo,
             LinkProperties linkProperties, NetworkCapabilities networkCapabilities,
             int currentScore, NetworkMisc networkMisc) {
@@ -4532,6 +4423,7 @@
                             mLegacyTypeTracker.remove(currentNetwork.networkInfo.getType(),
                                                       currentNetwork);
                         }
+                        mDefaultInetConditionPublished = 100;
                         mLegacyTypeTracker.add(newNetwork.networkInfo.getType(), newNetwork);
                     }
                 }
@@ -4581,8 +4473,6 @@
                 // to connected after our normal pause unless somebody reports us as
                 // really disconnected
                 mDefaultInetConditionPublished = 0;
-                mDefaultConnectionSequence++;
-                mInetConditionChangeInFlight = false;
                 // TODO - read the tcp buffer size config string from somewhere
                 // updateNetworkSettings();
             }
diff --git a/services/core/java/com/android/server/DockObserver.java b/services/core/java/com/android/server/DockObserver.java
index af38664..d05c280 100644
--- a/services/core/java/com/android/server/DockObserver.java
+++ b/services/core/java/com/android/server/DockObserver.java
@@ -201,9 +201,6 @@
             // There are many components in the system watching for this so as to
             // adjust audio routing, screen orientation, etc.
             getContext().sendStickyBroadcastAsUser(intent, UserHandle.ALL);
-
-            // Release the wake lock that was acquired when the message was posted.
-            mWakeLock.release();
         }
     }
 
@@ -213,6 +210,7 @@
             switch (msg.what) {
                 case MSG_DOCK_STATE_CHANGED:
                     handleDockStateChange();
+                    mWakeLock.release();
                     break;
             }
         }
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index b4faea1..5c77014 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -37,7 +37,6 @@
 import android.app.ActivityManager;
 import android.app.AppOpsManager;
 import android.content.Context;
-import android.content.pm.IPackageDeleteObserver;
 import android.content.pm.IPackageDeleteObserver2;
 import android.content.pm.IPackageInstaller;
 import android.content.pm.IPackageInstallerCallback;
@@ -199,6 +198,10 @@
         }
     }
 
+    public static boolean isStageFile(File file) {
+        return sStageFilter.accept(null, file.getName());
+    }
+
     @Deprecated
     public File allocateSessionDir() throws IOException {
         synchronized (mSessions) {
@@ -559,6 +562,15 @@
     }
 
     @Override
+    public void setPermissionsResult(int sessionId, boolean accepted) {
+        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES, TAG);
+
+        synchronized (mSessions) {
+            mSessions.get(sessionId).setPermissionsResult(accepted);
+        }
+    }
+
+    @Override
     public void registerCallback(IPackageInstallerCallback callback, int userId) {
         mPm.enforceCrossUserPermission(Binder.getCallingUid(), userId, true, "registerCallback");
         enforceCallerCanReadSessions();
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 5443fbc..92bb44b 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -20,6 +20,7 @@
 import static android.content.pm.PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
 import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_APK;
 import static android.content.pm.PackageManager.INSTALL_FAILED_PACKAGE_CHANGED;
+import static android.content.pm.PackageManager.INSTALL_FAILED_REJECTED;
 import static android.system.OsConstants.O_CREAT;
 import static android.system.OsConstants.O_RDONLY;
 import static android.system.OsConstants.O_WRONLY;
@@ -30,6 +31,7 @@
 import android.content.pm.IPackageInstallerSession;
 import android.content.pm.InstallSessionInfo;
 import android.content.pm.InstallSessionParams;
+import android.content.pm.PackageInstaller;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageParser;
 import android.content.pm.PackageParser.ApkLite;
@@ -106,10 +108,24 @@
     @GuardedBy("mLock")
     private boolean mSealed = false;
     @GuardedBy("mLock")
-    private boolean mPermissionsConfirmed = false;
+    private boolean mPermissionsAccepted = false;
     @GuardedBy("mLock")
     private boolean mDestroyed = false;
 
+    private int mFinalStatus;
+    private String mFinalMessage;
+
+    /**
+     * Path to the resolved base APK for this session, which may point at an APK
+     * inside the session (when the session defines the base), or it may point
+     * at the existing base APK (when adding splits to an existing app).
+     * <p>
+     * This is used when confirming permissions, since we can't fully stage the
+     * session inside an ASEC before confirming with user.
+     */
+    @GuardedBy("mLock")
+    private String mResolvedBaseCodePath;
+
     @GuardedBy("mLock")
     private ArrayList<FileBridge> mBridges = new ArrayList<>();
 
@@ -134,12 +150,7 @@
                 } catch (PackageManagerException e) {
                     Slog.e(TAG, "Install failed: " + e);
                     destroyInternal();
-                    try {
-                        mRemoteObserver.onPackageInstalled(mPackageName, e.error, e.getMessage(),
-                                null);
-                    } catch (RemoteException ignored) {
-                    }
-                    mCallback.onSessionFinished(PackageInstallerSession.this, false);
+                    dispatchSessionFinished(e.error, e.getMessage(), null);
                 }
 
                 return true;
@@ -169,9 +180,9 @@
 
         if (mPm.checkPermission(android.Manifest.permission.INSTALL_PACKAGES,
                 installerPackageName) == PackageManager.PERMISSION_GRANTED) {
-            mPermissionsConfirmed = true;
+            mPermissionsAccepted = true;
         } else {
-            mPermissionsConfirmed = false;
+            mPermissionsAccepted = false;
         }
 
         computeProgressLocked();
@@ -182,7 +193,9 @@
 
         info.sessionId = sessionId;
         info.installerPackageName = installerPackageName;
+        info.resolvedBaseCodePath = mResolvedBaseCodePath;
         info.progress = mProgress;
+        info.sealed = mSealed;
         info.open = openCount.get() > 0;
 
         info.mode = params.mode;
@@ -355,11 +368,19 @@
 
         Preconditions.checkNotNull(mPackageName);
         Preconditions.checkNotNull(mSignatures);
+        Preconditions.checkNotNull(mResolvedBaseCodePath);
 
-        if (!mPermissionsConfirmed) {
-            // TODO: async confirm permissions with user
-            // when they confirm, we'll kick off another install() pass
-            throw new SecurityException("Caller must hold INSTALL permission");
+        if (!mPermissionsAccepted) {
+            // User needs to accept permissions; give installer an intent they
+            // can use to involve user.
+            final Intent intent = new Intent(PackageInstaller.ACTION_CONFIRM_PERMISSIONS);
+            intent.setPackage("com.android.packageinstaller");
+            intent.putExtra(PackageInstaller.EXTRA_SESSION_ID, sessionId);
+            try {
+                mRemoteObserver.onUserActionRequired(intent);
+            } catch (RemoteException ignored) {
+            }
+            return;
         }
 
         // Inherit any packages and native libraries from existing install that
@@ -386,12 +407,7 @@
             public void onPackageInstalled(String basePackageName, int returnCode, String msg,
                     Bundle extras) {
                 destroyInternal();
-                try {
-                    remoteObserver.onPackageInstalled(basePackageName, returnCode, msg, extras);
-                } catch (RemoteException ignored) {
-                }
-                final boolean success = (returnCode == PackageManager.INSTALL_SUCCEEDED);
-                mCallback.onSessionFinished(PackageInstallerSession.this, success);
+                dispatchSessionFinished(returnCode, msg, extras);
             }
         };
 
@@ -409,6 +425,7 @@
         mPackageName = null;
         mVersionCode = -1;
         mSignatures = null;
+        mResolvedBaseCodePath = null;
 
         final File[] files = sessionStageDir.listFiles();
         if (ArrayUtils.isEmpty(files)) {
@@ -445,18 +462,25 @@
                     info.signatures);
 
             // Take this opportunity to enforce uniform naming
-            final String name;
+            final String targetName;
             if (info.splitName == null) {
-                name = "base.apk";
+                targetName = "base.apk";
             } else {
-                name = "split_" + info.splitName + ".apk";
+                targetName = "split_" + info.splitName + ".apk";
             }
-            if (!FileUtils.isValidExtFilename(name)) {
+            if (!FileUtils.isValidExtFilename(targetName)) {
                 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
-                        "Invalid filename: " + name);
+                        "Invalid filename: " + targetName);
             }
-            if (!file.getName().equals(name)) {
-                file.renameTo(new File(file.getParentFile(), name));
+
+            final File targetFile = new File(sessionStageDir, targetName);
+            if (!file.equals(targetFile)) {
+                file.renameTo(targetFile);
+            }
+
+            // Base is coming from session
+            if (info.splitName == null) {
+                mResolvedBaseCodePath = targetFile.getAbsolutePath();
             }
         }
 
@@ -472,13 +496,18 @@
             }
 
         } else {
-            // Partial installs must be consistent with existing install.
+            // Partial installs must be consistent with existing install
             final ApplicationInfo app = mPm.getApplicationInfo(mPackageName, 0, userId);
             if (app == null) {
                 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
                         "Missing existing base package for " + mPackageName);
             }
 
+            // Base might be inherited from existing install
+            if (mResolvedBaseCodePath == null) {
+                mResolvedBaseCodePath = app.getBaseCodePath();
+            }
+
             final ApkLite info;
             try {
                 info = PackageParser.parseApkLite(new File(app.getBaseCodePath()),
@@ -537,6 +566,21 @@
         if (LOGD) Slog.d(TAG, "Spliced " + n + " existing APKs into stage");
     }
 
+    void setPermissionsResult(boolean accepted) {
+        if (!mSealed) {
+            throw new SecurityException("Must be sealed to accept permissions");
+        }
+
+        if (accepted) {
+            // Mark and kick off another install pass
+            mPermissionsAccepted = true;
+            mHandler.obtainMessage(MSG_COMMIT).sendToTarget();
+        } else {
+            destroyInternal();
+            dispatchSessionFinished(INSTALL_FAILED_REJECTED, "User rejected permissions", null);
+        }
+    }
+
     @Override
     public void close() {
         if (openCount.decrementAndGet() == 0) {
@@ -546,11 +590,23 @@
 
     @Override
     public void abandon() {
-        try {
-            destroyInternal();
-        } finally {
-            mCallback.onSessionFinished(this, false);
+        destroyInternal();
+        dispatchSessionFinished(INSTALL_FAILED_INTERNAL_ERROR, "Session was abandoned", null);
+    }
+
+    private void dispatchSessionFinished(int returnCode, String msg, Bundle extras) {
+        mFinalStatus = returnCode;
+        mFinalMessage = msg;
+
+        if (mRemoteObserver != null) {
+            try {
+                mRemoteObserver.onPackageInstalled(mPackageName, returnCode, msg, extras);
+            } catch (RemoteException ignored) {
+            }
         }
+
+        final boolean success = (returnCode == PackageManager.INSTALL_SUCCEEDED);
+        mCallback.onSessionFinished(this, success);
     }
 
     private void destroyInternal() {
@@ -578,9 +634,11 @@
         pw.printPair("mClientProgress", mClientProgress);
         pw.printPair("mProgress", mProgress);
         pw.printPair("mSealed", mSealed);
-        pw.printPair("mPermissionsConfirmed", mPermissionsConfirmed);
+        pw.printPair("mPermissionsAccepted", mPermissionsAccepted);
         pw.printPair("mDestroyed", mDestroyed);
         pw.printPair("mBridges", mBridges.size());
+        pw.printPair("mFinalStatus", mFinalStatus);
+        pw.printPair("mFinalMessage", mFinalMessage);
         pw.println();
 
         pw.decreaseIndent();
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 4bf6636..6802fac 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -4084,7 +4084,8 @@
         }
 
         for (File file : files) {
-            final boolean isPackage = isApkFile(file) || file.isDirectory();
+            final boolean isPackage = (isApkFile(file) || file.isDirectory())
+                    && !PackageInstallerService.isStageFile(file);
             if (!isPackage) {
                 // Ignore entries which are not apk's
                 continue;
diff --git a/services/core/java/com/android/server/tv/TvInputHardwareManager.java b/services/core/java/com/android/server/tv/TvInputHardwareManager.java
index 74f725f..71f43b4 100644
--- a/services/core/java/com/android/server/tv/TvInputHardwareManager.java
+++ b/services/core/java/com/android/server/tv/TvInputHardwareManager.java
@@ -37,13 +37,12 @@
 import android.media.AudioPortConfig;
 import android.media.tv.ITvInputHardware;
 import android.media.tv.ITvInputHardwareCallback;
-import android.media.tv.TvInputHardwareInfo;
 import android.media.tv.TvContract;
+import android.media.tv.TvInputHardwareInfo;
 import android.media.tv.TvInputInfo;
 import android.media.tv.TvStreamConfig;
 import android.os.Handler;
 import android.os.IBinder;
-import android.os.Looper;
 import android.os.Message;
 import android.os.RemoteException;
 import android.os.ServiceManager;
@@ -59,12 +58,10 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
-import java.util.HashSet;
 import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
-import java.util.Set;
 
 /**
  * A helper class for TvInputManagerService to handle TV input hardware.
@@ -82,11 +79,11 @@
     private final TvInputHal mHal = new TvInputHal(this);
     private final SparseArray<Connection> mConnections = new SparseArray<>();
     private final List<TvInputHardwareInfo> mHardwareList = new ArrayList<>();
-    private List<HdmiDeviceInfo> mHdmiCecDeviceList = new LinkedList<>();
+    private final List<HdmiDeviceInfo> mHdmiDeviceList = new LinkedList<>();
     /* A map from a device ID to the matching TV input ID. */
     private final SparseArray<String> mHardwareInputIdMap = new SparseArray<>();
     /* A map from a HDMI logical address to the matching TV input ID. */
-    private final SparseArray<String> mHdmiCecInputIdMap = new SparseArray<>();
+    private final SparseArray<String> mHdmiInputIdMap = new SparseArray<>();
     private final Map<String, TvInputInfo> mInputMap = new ArrayMap<>();
 
     private final AudioManager mAudioManager;
@@ -119,7 +116,7 @@
                 try {
                     mHdmiControlService.addHotplugEventListener(mHdmiHotplugEventListener);
                     mHdmiControlService.addDeviceEventListener(mHdmiDeviceEventListener);
-                    mHdmiCecDeviceList.addAll(mHdmiControlService.getInputDevices());
+                    mHdmiDeviceList.addAll(mHdmiControlService.getInputDevices());
                     mHdmiControlService.setInputChangeListener(mHdmiInputChangeListener);
                 } catch (RemoteException e) {
                     Slog.w(TAG, "Error registering listeners to HdmiControlService:", e);
@@ -163,12 +160,11 @@
             buildHardwareListLocked();
             TvInputHardwareInfo info = connection.getHardwareInfoLocked();
             if (info.getType() == TvInputHardwareInfo.TV_INPUT_TYPE_HDMI) {
-                // Remove HDMI CEC devices linked with this hardware.
-                for (Iterator<HdmiDeviceInfo> it = mHdmiCecDeviceList.iterator();
-                        it.hasNext(); ) {
+                // Remove HDMI devices linked with this hardware.
+                for (Iterator<HdmiDeviceInfo> it = mHdmiDeviceList.iterator(); it.hasNext();) {
                     HdmiDeviceInfo deviceInfo = it.next();
                     if (deviceInfo.getPortId() == info.getHdmiPortId()) {
-                        mHandler.obtainMessage(ListenerHandler.HDMI_CEC_DEVICE_REMOVED, 0, 0,
+                        mHandler.obtainMessage(ListenerHandler.HDMI_DEVICE_REMOVED, 0, 0,
                                 deviceInfo).sendToTarget();
                         it.remove();
                     }
@@ -220,9 +216,9 @@
         }
     }
 
-    public List<HdmiDeviceInfo> getHdmiCecInputDeviceList() {
+    public List<HdmiDeviceInfo> getHdmiDeviceList() {
         synchronized (mLock) {
-            return Collections.unmodifiableList(mHdmiCecDeviceList);
+            return Collections.unmodifiableList(mHdmiDeviceList);
         }
     }
 
@@ -283,7 +279,7 @@
         return -1;
     }
 
-    public void addHdmiCecTvInput(int logicalAddress, TvInputInfo info) {
+    public void addHdmiTvInput(int logicalAddress, TvInputInfo info) {
         if (info.getType() != TvInputInfo.TYPE_HDMI) {
             throw new IllegalArgumentException("info (" + info + ") has non-HDMI type.");
         }
@@ -293,13 +289,13 @@
             if (parentIndex < 0) {
                 throw new IllegalArgumentException("info (" + info + ") has invalid parentId.");
             }
-            String oldInputId = mHdmiCecInputIdMap.get(logicalAddress);
+            String oldInputId = mHdmiInputIdMap.get(logicalAddress);
             if (oldInputId != null) {
                 Slog.w(TAG, "Trying to override previous registration: old = "
                         + mInputMap.get(oldInputId) + ":" + logicalAddress + ", new = "
                         + info + ":" + logicalAddress);
             }
-            mHdmiCecInputIdMap.put(logicalAddress, info.getId());
+            mHdmiInputIdMap.put(logicalAddress, info.getId());
             mInputMap.put(info.getId(), info);
         }
     }
@@ -311,9 +307,9 @@
             if (hardwareIndex >= 0) {
                 mHardwareInputIdMap.removeAt(hardwareIndex);
             }
-            int cecIndex = indexOfEqualValue(mHdmiCecInputIdMap, inputId);
-            if (cecIndex >= 0) {
-                mHdmiCecInputIdMap.removeAt(cecIndex);
+            int deviceIndex = indexOfEqualValue(mHdmiInputIdMap, inputId);
+            if (deviceIndex >= 0) {
+                mHdmiInputIdMap.removeAt(deviceIndex);
             }
         }
     }
@@ -864,16 +860,16 @@
         public void onStateChanged(String inputId, int state);
         public void onHardwareDeviceAdded(TvInputHardwareInfo info);
         public void onHardwareDeviceRemoved(TvInputHardwareInfo info);
-        public void onHdmiCecDeviceAdded(HdmiDeviceInfo cecDevice);
-        public void onHdmiCecDeviceRemoved(HdmiDeviceInfo cecDevice);
+        public void onHdmiDeviceAdded(HdmiDeviceInfo device);
+        public void onHdmiDeviceRemoved(HdmiDeviceInfo device);
     }
 
     private class ListenerHandler extends Handler {
         private static final int STATE_CHANGED = 1;
         private static final int HARDWARE_DEVICE_ADDED = 2;
         private static final int HARDWARE_DEVICE_REMOVED = 3;
-        private static final int HDMI_CEC_DEVICE_ADDED = 4;
-        private static final int HDMI_CEC_DEVICE_REMOVED = 5;
+        private static final int HDMI_DEVICE_ADDED = 4;
+        private static final int HDMI_DEVICE_REMOVED = 5;
 
         @Override
         public final void handleMessage(Message msg) {
@@ -894,14 +890,14 @@
                     mListener.onHardwareDeviceRemoved(info);
                     break;
                 }
-                case HDMI_CEC_DEVICE_ADDED: {
+                case HDMI_DEVICE_ADDED: {
                     HdmiDeviceInfo info = (HdmiDeviceInfo) msg.obj;
-                    mListener.onHdmiCecDeviceAdded(info);
+                    mListener.onHdmiDeviceAdded(info);
                     break;
                 }
-                case HDMI_CEC_DEVICE_REMOVED: {
+                case HDMI_DEVICE_REMOVED: {
                     HdmiDeviceInfo info = (HdmiDeviceInfo) msg.obj;
-                    mListener.onHdmiCecDeviceRemoved(info);
+                    mListener.onHdmiDeviceRemoved(info);
                     break;
                 }
                 default: {
@@ -939,21 +935,21 @@
         public void onStatusChanged(HdmiDeviceInfo deviceInfo, boolean activated) {
             synchronized (mLock) {
                 if (activated) {
-                    if (!mHdmiCecDeviceList.contains(deviceInfo)) {
-                        mHdmiCecDeviceList.add(deviceInfo);
+                    if (!mHdmiDeviceList.contains(deviceInfo)) {
+                        mHdmiDeviceList.add(deviceInfo);
                     } else {
                         Slog.w(TAG, "The list already contains " + deviceInfo + "; ignoring.");
                         return;
                     }
                 } else {
-                    if (!mHdmiCecDeviceList.remove(deviceInfo)) {
+                    if (!mHdmiDeviceList.remove(deviceInfo)) {
                         Slog.w(TAG, "The list doesn't contain " + deviceInfo + "; ignoring.");
                         return;
                     }
                 }
                 Message msg = mHandler.obtainMessage(
-                        activated ? ListenerHandler.HDMI_CEC_DEVICE_ADDED
-                        : ListenerHandler.HDMI_CEC_DEVICE_REMOVED,
+                        activated ? ListenerHandler.HDMI_DEVICE_ADDED
+                        : ListenerHandler.HDMI_DEVICE_REMOVED,
                         0, 0, deviceInfo);
                 if (findHardwareInfoForHdmiPortLocked(deviceInfo.getPortId()) != null) {
                     msg.sendToTarget();
@@ -970,7 +966,7 @@
             String inputId;
             synchronized (mLock) {
                 if (device.isCecDevice()) {
-                    inputId = mHdmiCecInputIdMap.get(device.getLogicalAddress());
+                    inputId = mHdmiInputIdMap.get(device.getLogicalAddress());
                 } else {
                     TvInputHardwareInfo hardwareInfo =
                             findHardwareInfoForHdmiPortLocked(device.getPortId());
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index 14d1ec4..c5e30d5 100644
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -1933,13 +1933,13 @@
                         }
                     }
 
-                    List<HdmiDeviceInfo> cecDeviceInfoList =
-                            mTvInputHardwareManager.getHdmiCecInputDeviceList();
-                    for (HdmiDeviceInfo cecDeviceInfo : cecDeviceInfoList) {
+                    List<HdmiDeviceInfo> deviceInfoList =
+                            mTvInputHardwareManager.getHdmiDeviceList();
+                    for (HdmiDeviceInfo deviceInfo : deviceInfoList) {
                         try {
-                            serviceState.mService.notifyHdmiCecDeviceAdded(cecDeviceInfo);
+                            serviceState.mService.notifyHdmiDeviceAdded(deviceInfo);
                         } catch (RemoteException e) {
-                            Slog.e(TAG, "error in notifyHdmiCecDeviceAdded", e);
+                            Slog.e(TAG, "error in notifyHdmiDeviceAdded", e);
                         }
                     }
                 }
@@ -2025,11 +2025,11 @@
         }
 
         @Override
-        public void addHdmiCecTvInput(int logicalAddress, TvInputInfo inputInfo) {
+        public void addHdmiTvInput(int logicalAddress, TvInputInfo inputInfo) {
             ensureHardwarePermission();
             ensureValidInput(inputInfo);
             synchronized (mLock) {
-                mTvInputHardwareManager.addHdmiCecTvInput(logicalAddress, inputInfo);
+                mTvInputHardwareManager.addHdmiTvInput(logicalAddress, inputInfo);
                 addTvInputLocked(inputInfo);
             }
         }
@@ -2275,32 +2275,32 @@
         }
 
         @Override
-        public void onHdmiCecDeviceAdded(HdmiDeviceInfo cecDeviceInfo) {
+        public void onHdmiDeviceAdded(HdmiDeviceInfo deviceInfo) {
             synchronized (mLock) {
                 UserState userState = getUserStateLocked(mCurrentUserId);
                 // Broadcast the event to all hardware inputs.
                 for (ServiceState serviceState : userState.serviceStateMap.values()) {
                     if (!serviceState.mIsHardware || serviceState.mService == null) continue;
                     try {
-                        serviceState.mService.notifyHdmiCecDeviceAdded(cecDeviceInfo);
+                        serviceState.mService.notifyHdmiDeviceAdded(deviceInfo);
                     } catch (RemoteException e) {
-                        Slog.e(TAG, "error in notifyHdmiCecDeviceAdded", e);
+                        Slog.e(TAG, "error in notifyHdmiDeviceAdded", e);
                     }
                 }
             }
         }
 
         @Override
-        public void onHdmiCecDeviceRemoved(HdmiDeviceInfo cecDeviceInfo) {
+        public void onHdmiDeviceRemoved(HdmiDeviceInfo deviceInfo) {
             synchronized (mLock) {
                 UserState userState = getUserStateLocked(mCurrentUserId);
                 // Broadcast the event to all hardware inputs.
                 for (ServiceState serviceState : userState.serviceStateMap.values()) {
                     if (!serviceState.mIsHardware || serviceState.mService == null) continue;
                     try {
-                        serviceState.mService.notifyHdmiCecDeviceRemoved(cecDeviceInfo);
+                        serviceState.mService.notifyHdmiDeviceRemoved(deviceInfo);
                     } catch (RemoteException e) {
-                        Slog.e(TAG, "error in notifyHdmiCecDeviceRemoved", e);
+                        Slog.e(TAG, "error in notifyHdmiDeviceRemoved", e);
                     }
                 }
             }
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable05.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable05.xml
index 5b1f6ab..bbf1a17 100644
--- a/tests/VectorDrawableTest/res/drawable/vector_drawable05.xml
+++ b/tests/VectorDrawableTest/res/drawable/vector_drawable05.xml
@@ -28,8 +28,7 @@
                 l-5.046875,0.0 0.0-1.0Z" />
         <path
             android:name="two"
-            android:fillColor="#ffff00"
-            android:fillOpacity="0"
+            android:fillColor="#00ffff00"
             android:pathData="M 2.534375,9.6875l 4.140625,0.0 0.0,1.0-5.5625,0.0 0.0-1.0q 0.671875-0.6875 1.828125-1.859375
                         q 1.1718752-1.1875 1.4687502-1.53125 0.578125-0.625 0.796875-1.0625
                         q 0.234375-0.453125 0.234375-0.875 0.0-0.703125-0.5-1.140625
diff --git a/tests/VectorDrawableTest/res/drawable/vector_icon_schedule.xml b/tests/VectorDrawableTest/res/drawable/vector_icon_schedule.xml
index 8cabca8..1aad743 100644
--- a/tests/VectorDrawableTest/res/drawable/vector_icon_schedule.xml
+++ b/tests/VectorDrawableTest/res/drawable/vector_icon_schedule.xml
@@ -21,10 +21,10 @@
 
     <group>
         <path
-            android:fillOpacity="0.9"
+            android:fillColor="#E6000000"
             android:pathData="M11.994999,2.0C6.4679985,2.0 2.0,6.4780006 2.0,12.0s4.468,10.0 9.995,10.0S22.0,17.522 22.0,12.0S17.521,2.0 11.994999,2.0zM12.0,20.0c-4.42,0.0 -8.0,-3.582 -8.0,-8.0s3.58,-8.0 8.0,-8.0s8.0,3.582 8.0,8.0S16.419998,20.0 12.0,20.0z" />
         <path
-            android:fillOpacity="0.9"
+            android:fillColor="#E6000000"
             android:pathData="M12.5,6.0l-1.5,0.0 0.0,7.0 5.3029995,3.1819992 0.75,-1.249999 -4.5529995,-2.7320004z" />
     </group>
 
diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp
index 4f1d15e..ec284c5 100644
--- a/tools/aapt/Resource.cpp
+++ b/tools/aapt/Resource.cpp
@@ -1739,7 +1739,7 @@
 
 static status_t writeLayoutClasses(
     FILE* fp, const sp<AaptAssets>& assets,
-    const sp<AaptSymbols>& symbols, int indent, bool includePrivate)
+    const sp<AaptSymbols>& symbols, int indent, bool includePrivate, bool nonConstantId)
 {
     const char* indentStr = getIndentSpace(indent);
     if (!includePrivate) {
@@ -1957,8 +1957,13 @@
                         getSymbolName(name8).string());
                 fprintf(fp, "%s*/\n", indentStr);
                 ann.printAnnotations(fp, indentStr);
+
+                const char * id_format = nonConstantId ?
+                        "%spublic static int %s_%s = %d;\n" :
+                        "%spublic static final int %s_%s = %d;\n";
+
                 fprintf(fp,
-                        "%spublic static final int %s_%s = %d;\n",
+                        id_format,
                         indentStr, nclassName.string(),
                         flattenSymbol(name8).string(), (int)pos);
             }
@@ -2177,7 +2182,7 @@
     }
 
     if (styleableSymbols != NULL) {
-        err = writeLayoutClasses(fp, assets, styleableSymbols, indent, includePrivate);
+        err = writeLayoutClasses(fp, assets, styleableSymbols, indent, includePrivate, nonConstantId);
         if (err != NO_ERROR) {
             return err;
         }
diff --git a/tools/layoutlib/rename_font/build_font_single.py b/tools/layoutlib/rename_font/build_font_single.py
new file mode 100755
index 0000000..d648b04
--- /dev/null
+++ b/tools/layoutlib/rename_font/build_font_single.py
@@ -0,0 +1,207 @@
+#!/usr/bin/env python
+
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the 'License');
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an 'AS IS' BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""
+Rename the PS name of the input font.
+
+OpenType fonts (*.otf) are not currently supported. They are copied to the destination without renaming.
+XML files are also copied in case they are passed there by mistake.
+
+Usage: build_font.py /path/to/input_font.ttf /path/to/output_font.ttf
+
+"""
+
+import glob
+import os
+import re
+import shutil
+import sys
+import xml.etree.ElementTree as etree
+
+# Prevent .pyc files from being created.
+sys.dont_write_bytecode = True
+
+# fontTools is available at platform/external/fonttools
+from fontTools import ttx
+
+
+class FontInfo(object):
+  family = None
+  style = None
+  version = None
+  ends_in_regular = False
+  fullname = None
+
+
+class InvalidFontException(Exception):
+  pass
+
+
+# A constant to copy the font without modifying. This is useful when running
+# locally and speed up the time to build the SDK.
+COPY_ONLY = False
+
+# These constants represent the value of nameID parameter in the namerecord for
+# different information.
+# see http://scripts.sil.org/cms/scripts/page.php?item_id=IWS-Chapter08#3054f18b
+NAMEID_FAMILY = 1
+NAMEID_STYLE = 2
+NAMEID_FULLNAME = 4
+NAMEID_VERSION = 5
+
+# A list of extensions to process.
+EXTENSIONS = ['.ttf', '.otf', '.xml']
+
+def main(argv):
+  if len(argv) < 2:
+    sys.exit('Usage: build_font.py /path/to/input/font.ttf /path/to/out/font.ttf')
+  dest_path = argv[-1]
+  input_path = argv[0]
+  extension = os.path.splitext(input_path)[1].lower()
+  if extension in EXTENSIONS:
+    if not COPY_ONLY and extension == '.ttf':
+      convert_font(input_path, dest_path)
+      return
+    shutil.copy(input_path, dest_path)
+
+
+def convert_font(input_path, dest_path):
+  filename = os.path.basename(input_path)
+  print 'Converting font: ' + filename
+  # the path to the output file. The file name is the fontfilename.ttx
+  ttx_path = dest_path[:-1] + 'x'
+  try:
+    # run ttx to generate an xml file in the output folder which represents all
+    # its info
+    ttx_args = ['-q', '-o', ttx_path, input_path]
+    ttx.main(ttx_args)
+    # now parse the xml file to change its PS name.
+    tree = etree.parse(ttx_path)
+    root = tree.getroot()
+    for name in root.iter('name'):
+      update_tag(name, get_font_info(name))
+    tree.write(ttx_path, xml_declaration=True, encoding='utf-8')
+    # generate the udpated font now.
+    ttx_args = ['-q', '-o', dest_path, ttx_path]
+    ttx.main(ttx_args)
+  except InvalidFontException:
+    # In case of invalid fonts, we exit.
+    print filename + ' is not a valid font'
+    raise
+  except Exception as e:
+    print 'Error converting font: ' + filename
+    print e
+    # Some fonts are too big to be handled by the ttx library.
+    # Just copy paste them.
+    shutil.copy(input_path, dest_path)
+  try:
+    # delete the temp ttx file is it exists.
+    os.remove(ttx_path)
+  except OSError:
+    pass
+
+
+def get_font_info(tag):
+  """ Returns a list of FontInfo representing the various sets of namerecords
+      found in the name table of the font. """
+  fonts = []
+  font = None
+  last_name_id = sys.maxint
+  for namerecord in tag.iter('namerecord'):
+    if 'nameID' in namerecord.attrib:
+      name_id = int(namerecord.attrib['nameID'])
+      # A new font should be created for each platform, encoding and language
+      # id. But, since the nameIDs are sorted, we use the easy approach of
+      # creating a new one when the nameIDs reset.
+      if name_id <= last_name_id and font is not None:
+        fonts.append(font)
+        font = None
+      last_name_id = name_id
+      if font is None:
+        font = FontInfo()
+      if name_id == NAMEID_FAMILY:
+        font.family = namerecord.text.strip()
+      if name_id == NAMEID_STYLE:
+        font.style = namerecord.text.strip()
+      if name_id == NAMEID_FULLNAME:
+        font.ends_in_regular = ends_in_regular(namerecord.text)
+        font.fullname = namerecord.text.strip()
+      if name_id == NAMEID_VERSION:
+        font.version = get_version(namerecord.text)
+  if font is not None:
+    fonts.append(font)
+  return fonts
+
+
+def update_tag(tag, fonts):
+  last_name_id = sys.maxint
+  fonts_iterator = fonts.__iter__()
+  font = None
+  for namerecord in tag.iter('namerecord'):
+    if 'nameID' in namerecord.attrib:
+      name_id = int(namerecord.attrib['nameID'])
+      if name_id <= last_name_id:
+        font = fonts_iterator.next()
+        font = update_font_name(font)
+      last_name_id = name_id
+      if name_id == NAMEID_FAMILY:
+        namerecord.text = font.family
+      if name_id == NAMEID_FULLNAME:
+        namerecord.text = font.fullname
+
+
+def update_font_name(font):
+  """ Compute the new font family name and font fullname. If the font has a
+      valid version, it's sanitized and appended to the font family name. The
+      font fullname is then created by joining the new family name and the
+      style. If the style is 'Regular', it is appended only if the original font
+      had it. """
+  if font.family is None or font.style is None:
+    raise InvalidFontException('Font doesn\'t have proper family name or style')
+  if font.version is not None:
+    new_family = font.family + font.version
+  else:
+    new_family = font.family
+  if font.style is 'Regular' and not font.ends_in_regular:
+    font.fullname = new_family
+  else:
+    font.fullname = new_family + ' ' + font.style
+  font.family = new_family
+  return font
+
+
+def ends_in_regular(string):
+  """ According to the specification, the font fullname should not end in
+      'Regular' for plain fonts. However, some fonts don't obey this rule. We
+      keep the style info, to minimize the diff. """
+  string = string.strip().split()[-1]
+  return string is 'Regular'
+
+
+def get_version(string):
+  # The string must begin with 'Version n.nn '
+  # to extract n.nn, we return the second entry in the split strings.
+  string = string.strip()
+  if not string.startswith('Version '):
+    raise InvalidFontException('mal-formed font version')
+  return sanitize(string.split()[1])
+
+
+def sanitize(string):
+  return re.sub(r'[^\w-]+', '', string)
+
+if __name__ == '__main__':
+  main(sys.argv[1:])