Merge changes Ia0f54124,I1e085f5e,Ic0a3ff0a,I141847df,I8ae4efa2 into lmp-preview-dev

* changes:
  camera2: Update native/managed key mappings.
  Camera2: switch API interface to java classses
  Camera2: Use ColorSpaceTransform and RggbChannelVector, replace enum byte[]
  camera2: Add new metadata keys, change types for existing range keys
  Camera2: Replace int[] by MeteringRectangle
diff --git a/Android.mk b/Android.mk
index 62d5446..8603d99 100644
--- a/Android.mk
+++ b/Android.mk
@@ -78,6 +78,7 @@
 	core/java/android/app/IServiceConnection.aidl \
 	core/java/android/app/IStopUserCallback.aidl \
 	core/java/android/app/task/ITaskCallback.aidl \
+	core/java/android/app/task/ITaskManager.aidl \
 	core/java/android/app/task/ITaskService.aidl \
 	core/java/android/app/IThumbnailRetriever.aidl \
 	core/java/android/app/ITransientNotification.aidl \
@@ -328,6 +329,7 @@
 	telecomm/java/com/android/internal/telecomm/ICallServiceSelectorAdapter.aidl \
 	telecomm/java/com/android/internal/telecomm/IInCallAdapter.aidl \
 	telecomm/java/com/android/internal/telecomm/IInCallService.aidl \
+	telecomm/java/com/android/internal/telecomm/ITelecommService.aidl \
 	telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl \
 	telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl \
 	telephony/java/com/android/internal/telephony/ITelephony.aidl \
diff --git a/api/current.txt b/api/current.txt
index 441ad26..cddbbf8 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -4689,6 +4689,7 @@
     method public android.app.Notification.WearableExtender setHintShowBackgroundOnly(boolean);
     method public android.app.Notification.WearableExtender setStartScrollBottom(boolean);
     field public static final int SIZE_DEFAULT = 0; // 0x0
+    field public static final int SIZE_FULL_SCREEN = 5; // 0x5
     field public static final int SIZE_LARGE = 4; // 0x4
     field public static final int SIZE_MEDIUM = 3; // 0x3
     field public static final int SIZE_SMALL = 2; // 0x2
@@ -5350,6 +5351,57 @@
 
 package android.app.task {
 
+  public class Task implements android.os.Parcelable {
+    method public int describeContents();
+    method public int getBackoffPolicy();
+    method public android.os.Bundle getExtras();
+    method public long getInitialBackoffMillis();
+    method public long getIntervalMillis();
+    method public long getMaxExecutionDelayMillis();
+    method public long getMinLatencyMillis();
+    method public int getNetworkCapabilities();
+    method public android.content.ComponentName getService();
+    method public int getTaskId();
+    method public boolean isPeriodic();
+    method public boolean isRequireCharging();
+    method public boolean isRequireDeviceIdle();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator CREATOR;
+  }
+
+  public static abstract interface Task.BackoffPolicy {
+    field public static final int EXPONENTIAL = 1; // 0x1
+    field public static final int LINEAR = 0; // 0x0
+  }
+
+  public final class Task.Builder {
+    ctor public Task.Builder(int, android.content.ComponentName);
+    method public android.app.task.Task build();
+    method public android.app.task.Task.Builder setBackoffCriteria(long, int);
+    method public android.app.task.Task.Builder setExtras(android.os.Bundle);
+    method public android.app.task.Task.Builder setMinimumLatency(long);
+    method public android.app.task.Task.Builder setOverrideDeadline(long);
+    method public android.app.task.Task.Builder setPeriodic(long);
+    method public android.app.task.Task.Builder setRequiredNetworkCapabilities(int);
+    method public android.app.task.Task.Builder setRequiresCharging(boolean);
+    method public android.app.task.Task.Builder setRequiresDeviceIdle(boolean);
+  }
+
+  public static abstract interface Task.NetworkType {
+    field public static final int ANY = 0; // 0x0
+    field public static final int UNMETERED = 1; // 0x1
+  }
+
+  public abstract class TaskManager {
+    ctor public TaskManager();
+    method public abstract void cancel(int);
+    method public abstract void cancelAll();
+    method public abstract java.util.List<android.app.task.Task> getAllPendingTasks();
+    method public abstract int schedule(android.app.task.Task);
+    field public static final int RESULT_INVALID_PARAMETERS = -1; // 0xffffffff
+    field public static final int RESULT_OVER_QUOTA = -2; // 0xfffffffe
+  }
+
   public class TaskParams implements android.os.Parcelable {
     method public int describeContents();
     method public android.os.Bundle getExtras();
@@ -6973,6 +7025,7 @@
     field public static final java.lang.String SEARCH_SERVICE = "search";
     field public static final java.lang.String SENSOR_SERVICE = "sensor";
     field public static final java.lang.String STORAGE_SERVICE = "storage";
+    field public static final java.lang.String TASK_SERVICE = "task";
     field public static final java.lang.String TELEPHONY_SERVICE = "phone";
     field public static final java.lang.String TEXT_SERVICES_MANAGER_SERVICE = "textservices";
     field public static final java.lang.String TV_INPUT_SERVICE = "tv_input";
@@ -7911,55 +7964,6 @@
     method public abstract void onStatusChanged(int);
   }
 
-  public class Task implements android.os.Parcelable {
-    method public int describeContents();
-    method public int getBackoffPolicy();
-    method public android.os.Bundle getExtras();
-    method public long getInitialBackoffMillis();
-    method public long getIntervalMillis();
-    method public long getMaxExecutionDelayMillis();
-    method public long getMinLatencyMillis();
-    method public int getNetworkCapabilities();
-    method public android.content.ComponentName getService();
-    method public int getTaskId();
-    method public boolean isPeriodic();
-    method public boolean isRequireCharging();
-    method public boolean isRequireDeviceIdle();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator CREATOR;
-  }
-
-  public static abstract interface Task.BackoffPolicy {
-    field public static final int EXPONENTIAL = 1; // 0x1
-    field public static final int LINEAR = 0; // 0x0
-  }
-
-  public final class Task.Builder {
-    ctor public Task.Builder(int, android.content.ComponentName);
-    method public android.content.Task build();
-    method public android.content.Task.Builder setBackoffCriteria(long, int);
-    method public android.content.Task.Builder setExtras(android.os.Bundle);
-    method public android.content.Task.Builder setMinimumLatency(long);
-    method public android.content.Task.Builder setOverrideDeadline(long);
-    method public android.content.Task.Builder setPeriodic(long);
-    method public android.content.Task.Builder setRequiredNetworkCapabilities(int);
-    method public android.content.Task.Builder setRequiresCharging(boolean);
-    method public android.content.Task.Builder setRequiresDeviceIdle(boolean);
-  }
-
-  public static abstract interface Task.NetworkType {
-    field public static final int ANY = 0; // 0x0
-    field public static final int UNMETERED = 1; // 0x1
-  }
-
-  public abstract class TaskManager {
-    ctor public TaskManager();
-    method public abstract void cancel(int);
-    method public abstract void cancelAll();
-    method public abstract java.util.List<android.content.Task> getAllPendingTasks();
-    method public abstract int schedule(android.content.Task);
-  }
-
   public class UriMatcher {
     ctor public UriMatcher(int);
     method public void addURI(java.lang.String, java.lang.String, int);
@@ -13712,45 +13716,6 @@
     method public void stop();
   }
 
-  public final class AudioAttributes {
-    method public int getContentType();
-    method public int getFlags();
-    method public java.util.Set<java.lang.String> getTags();
-    method public int getUsage();
-    field public static final int CONTENT_TYPE_MOVIE = 3; // 0x3
-    field public static final int CONTENT_TYPE_MUSIC = 2; // 0x2
-    field public static final int CONTENT_TYPE_SONIFICATION = 4; // 0x4
-    field public static final int CONTENT_TYPE_SPEECH = 1; // 0x1
-    field public static final int CONTENT_TYPE_UNKNOWN = 0; // 0x0
-    field public static final int FLAG_AUDIBILITY_ENFORCED = 1; // 0x1
-    field public static final int USAGE_ALARM = 4; // 0x4
-    field public static final int USAGE_ASSISTANCE_ACCESSIBILITY = 11; // 0xb
-    field public static final int USAGE_ASSISTANCE_NAVIGATION_GUIDANCE = 12; // 0xc
-    field public static final int USAGE_ASSISTANCE_SONIFICATION = 13; // 0xd
-    field public static final int USAGE_GAME = 14; // 0xe
-    field public static final int USAGE_MEDIA = 1; // 0x1
-    field public static final int USAGE_NOTIFICATION = 5; // 0x5
-    field public static final int USAGE_NOTIFICATION_COMMUNICATION_DELAYED = 9; // 0x9
-    field public static final int USAGE_NOTIFICATION_COMMUNICATION_INSTANT = 8; // 0x8
-    field public static final int USAGE_NOTIFICATION_COMMUNICATION_REQUEST = 7; // 0x7
-    field public static final int USAGE_NOTIFICATION_EVENT = 10; // 0xa
-    field public static final int USAGE_NOTIFICATION_TELEPHONY_RINGTONE = 6; // 0x6
-    field public static final int USAGE_UNKNOWN = 0; // 0x0
-    field public static final int USAGE_VOICE_COMMUNICATION = 2; // 0x2
-    field public static final int USAGE_VOICE_COMMUNICATION_SIGNALLING = 3; // 0x3
-  }
-
-  public static class AudioAttributes.Builder {
-    ctor public AudioAttributes.Builder();
-    ctor public AudioAttributes.Builder(android.media.AudioAttributes);
-    method public android.media.AudioAttributes.Builder addTag(java.lang.String);
-    method public android.media.AudioAttributes build();
-    method public android.media.AudioAttributes.Builder setContentType(int);
-    method public android.media.AudioAttributes.Builder setFlags(int);
-    method public android.media.AudioAttributes.Builder setLegacyStreamType(int);
-    method public android.media.AudioAttributes.Builder setUsage(int);
-  }
-
   public class AudioFormat {
     ctor public AudioFormat();
     field public static final deprecated int CHANNEL_CONFIGURATION_DEFAULT = 1; // 0x1
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 07de85c..b5281ff 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -4194,7 +4194,11 @@
      */
     public void startActivityFromFragment(@NonNull Fragment fragment, Intent intent,
             int requestCode) {
-        startActivityFromFragment(fragment, intent, requestCode, null);
+        Bundle options = null;
+        if (mWindow.hasFeature(Window.FEATURE_CONTENT_TRANSITIONS)) {
+            options = ActivityOptions.makeSceneTransitionAnimation(this).toBundle();
+        }
+        startActivityFromFragment(fragment, intent, requestCode, options);
     }
 
     /**
@@ -4219,6 +4223,9 @@
      */
     public void startActivityFromFragment(@NonNull Fragment fragment, Intent intent,
             int requestCode, @Nullable Bundle options) {
+        if (options != null) {
+            mActivityTransitionState.startExitOutTransition(this, options);
+        }
         Instrumentation.ActivityResult ar =
             mInstrumentation.execStartActivity(
                 this, mMainThread.getApplicationThread(), mToken, fragment,
diff --git a/core/java/android/app/ActivityTransitionCoordinator.java b/core/java/android/app/ActivityTransitionCoordinator.java
index 7617886..d08978bd 100644
--- a/core/java/android/app/ActivityTransitionCoordinator.java
+++ b/core/java/android/app/ActivityTransitionCoordinator.java
@@ -189,15 +189,17 @@
     final protected SharedElementListener mListener;
     protected ResultReceiver mResultReceiver;
     final private FixedEpicenterCallback mEpicenterCallback = new FixedEpicenterCallback();
+    final protected boolean mIsReturning;
 
     public ActivityTransitionCoordinator(Window window,
             ArrayList<String> allSharedElementNames,
             ArrayList<String> accepted, ArrayList<String> localNames,
-            SharedElementListener listener) {
+            SharedElementListener listener, boolean isReturning) {
         super(new Handler());
         mWindow = window;
         mListener = listener;
         mAllSharedElementNames = allSharedElementNames;
+        mIsReturning = isReturning;
         setSharedElements(accepted, localNames);
         if (getViewsTransition() != null) {
             getDecor().captureTransitioningViews(mTransitioningViews);
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 1634d11..ff8688d 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -133,10 +133,12 @@
 import android.accounts.AccountManager;
 import android.accounts.IAccountManager;
 import android.app.admin.DevicePolicyManager;
+import android.app.task.ITaskManager;
 import android.app.trust.TrustManager;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.app.IAppOpsService;
+import com.android.internal.appwidget.IAppWidgetService.Stub;
 import com.android.internal.os.IDropBoxManagerService;
 
 import java.io.File;
@@ -693,6 +695,12 @@
                 public Object createService(ContextImpl ctx) {
                 return new UsageStatsManager(ctx.getOuterContext());
         }});
+
+        registerService(TASK_SERVICE, new ServiceFetcher() {
+            public Object createService(ContextImpl ctx) {
+                IBinder b = ServiceManager.getService(TASK_SERVICE);
+                return new TaskManagerImpl(ITaskManager.Stub.asInterface(b));
+            }});
     }
 
     static ContextImpl getImpl(Context context) {
diff --git a/core/java/android/app/EnterTransitionCoordinator.java b/core/java/android/app/EnterTransitionCoordinator.java
index 4cca355..bc97852 100644
--- a/core/java/android/app/EnterTransitionCoordinator.java
+++ b/core/java/android/app/EnterTransitionCoordinator.java
@@ -55,16 +55,14 @@
     private boolean mHasStopped;
     private Handler mHandler;
     private boolean mIsCanceled;
-    private boolean mIsReturning;
     private ObjectAnimator mBackgroundAnimator;
 
     public EnterTransitionCoordinator(Activity activity, ResultReceiver resultReceiver,
             ArrayList<String> sharedElementNames,
             ArrayList<String> acceptedNames, ArrayList<String> mappedNames) {
         super(activity.getWindow(), sharedElementNames, acceptedNames, mappedNames,
-                getListener(activity, acceptedNames));
+                getListener(activity, acceptedNames), acceptedNames != null);
         mActivity = activity;
-        mIsReturning = acceptedNames != null;
         setResultReceiver(resultReceiver);
         prepareEnter();
         Bundle resultReceiverBundle = new Bundle();
diff --git a/core/java/android/app/ExitTransitionCoordinator.java b/core/java/android/app/ExitTransitionCoordinator.java
index f36c36a..93eb53e 100644
--- a/core/java/android/app/ExitTransitionCoordinator.java
+++ b/core/java/android/app/ExitTransitionCoordinator.java
@@ -58,16 +58,14 @@
 
     private Handler mHandler;
 
-    private boolean mIsReturning;
-
     private ObjectAnimator mBackgroundAnimator;
 
     private boolean mIsHidden;
 
     public ExitTransitionCoordinator(Activity activity, ArrayList<String> names,
             ArrayList<String> accepted, ArrayList<String> mapped, boolean isReturning) {
-        super(activity.getWindow(), names, accepted, mapped, getListener(activity, isReturning));
-        mIsReturning = isReturning;
+        super(activity.getWindow(), names, accepted, mapped, getListener(activity, isReturning),
+                isReturning);
         mIsBackgroundReady = !isReturning;
         mActivity = activity;
     }
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 70f270f..25f24b1 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -3355,6 +3355,14 @@
          */
         public static final int SIZE_LARGE = 4;
 
+        /**
+         * Size value for use with {@link #setCustomSizePreset} to show this notification
+         * full screen.
+         * <p>This value is only applicable for custom display notifications created using
+         * {@link #setDisplayIntent}.
+         */
+        public static final int SIZE_FULL_SCREEN = 5;
+
         /** Notification extra which contains wearable extensions */
         private static final String EXTRA_WEARABLE_EXTENSIONS = "android.wearable.EXTENSIONS";
 
diff --git a/core/java/android/app/TaskManagerImpl.java b/core/java/android/app/TaskManagerImpl.java
new file mode 100644
index 0000000..f42839e
--- /dev/null
+++ b/core/java/android/app/TaskManagerImpl.java
@@ -0,0 +1,62 @@
+/*
+ * 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.
+ */
+
+// in android.app so ContextImpl has package access
+package android.app;
+
+import android.app.task.ITaskManager;
+import android.app.task.Task;
+import android.app.task.TaskManager;
+
+import java.util.List;
+
+
+/**
+ * Concrete implementation of the TaskManager interface
+ * @hide 
+ */
+public class TaskManagerImpl extends TaskManager {
+    ITaskManager mBinder;
+
+    /* package */ TaskManagerImpl(ITaskManager binder) {
+        mBinder = binder;
+    }
+
+    @Override
+    public int schedule(Task task) {
+        // TODO Auto-generated method stub
+        return 0;
+    }
+
+    @Override
+    public void cancel(int taskId) {
+        // TODO Auto-generated method stub
+
+    }
+
+    @Override
+    public void cancelAll() {
+        // TODO Auto-generated method stub
+
+    }
+
+    @Override
+    public List<Task> getAllPendingTasks() {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+}
diff --git a/core/java/android/app/task/ITaskManager.aidl b/core/java/android/app/task/ITaskManager.aidl
new file mode 100644
index 0000000..b56c78a
--- /dev/null
+++ b/core/java/android/app/task/ITaskManager.aidl
@@ -0,0 +1,30 @@
+/**
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.task;
+
+import android.app.task.Task;
+
+ /**
+  * IPC interface that supports the app-facing {@link #TaskManager} api.
+  * {@hide}
+  */
+interface ITaskManager {
+    int schedule(in Task task);
+    void cancel(int taskId);
+    void cancelAll();
+    List<Task> getAllPendingTasks();
+}
diff --git a/core/java/android/app/task/Task.aidl b/core/java/android/app/task/Task.aidl
new file mode 100644
index 0000000..1f25439
--- /dev/null
+++ b/core/java/android/app/task/Task.aidl
@@ -0,0 +1,20 @@
+/**
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.task;
+
+parcelable Task;
+ 
\ No newline at end of file
diff --git a/core/java/android/content/Task.java b/core/java/android/app/task/Task.java
similarity index 96%
rename from core/java/android/content/Task.java
rename to core/java/android/app/task/Task.java
index 407880f..dd184a5 100644
--- a/core/java/android/content/Task.java
+++ b/core/java/android/app/task/Task.java
@@ -14,15 +14,15 @@
  * limitations under the License
  */
 
-package android.content;
+package android.app.task;
 
-import android.app.task.TaskService;
+import android.content.ComponentName;
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
 
 /**
- * Container of data passed to the {@link android.content.TaskManager} fully encapsulating the
+ * Container of data passed to the {@link android.app.task.TaskManager} fully encapsulating the
  * parameters required to schedule work against the calling application. These are constructed
  * using the {@link Task.Builder}.
  */
@@ -92,7 +92,7 @@
     }
 
     /**
-     * See {@link android.content.Task.NetworkType} for a description of this value.
+     * See {@link android.app.task.Task.NetworkType} for a description of this value.
      */
     public int getNetworkCapabilities() {
         return networkCapabilities;
@@ -139,7 +139,7 @@
     }
 
     /**
-     * See {@link android.content.Task.BackoffPolicy} for an explanation of the values this field
+     * See {@link android.app.task.Task.BackoffPolicy} for an explanation of the values this field
      * can take. This defaults to exponential.
      */
     public int getBackoffPolicy() {
@@ -255,7 +255,7 @@
 
         /**
          * Set some description of the kind of network capabilities you would like to have. This
-         * will be a parameter defined in {@link android.content.Task.NetworkType}.
+         * will be a parameter defined in {@link android.app.task.Task.NetworkType}.
          * Not calling this function means the network is not necessary.
          * Bear in mind that calling this function defines network as a strict requirement for your
          * task if the network requested is not available your task will never run. See
@@ -314,7 +314,7 @@
          * Specify that this task should be delayed by the provided amount of time.
          * Because it doesn't make sense setting this property on a periodic task, doing so will
          * throw an {@link java.lang.IllegalArgumentException} when
-         * {@link android.content.Task.Builder#build()} is called.
+         * {@link android.app.task.Task.Builder#build()} is called.
          * @param minLatencyMillis Milliseconds before which this task will not be considered for
          *                         execution.
          */
@@ -328,7 +328,7 @@
          * deadline even if other requirements are not met. Because it doesn't make sense setting
          * this property on a periodic task, doing so will throw an
          * {@link java.lang.IllegalArgumentException} when
-         * {@link android.content.Task.Builder#build()} is called.
+         * {@link android.app.task.Task.Builder#build()} is called.
          */
         public Builder setOverrideDeadline(long maxExecutionDelayMillis) {
             mMaxExecutionDelayMillis = maxExecutionDelayMillis;
diff --git a/core/java/android/content/TaskManager.java b/core/java/android/app/task/TaskManager.java
similarity index 75%
rename from core/java/android/content/TaskManager.java
rename to core/java/android/app/task/TaskManager.java
index d28d78a..0fbe37d 100644
--- a/core/java/android/content/TaskManager.java
+++ b/core/java/android/app/task/TaskManager.java
@@ -14,14 +14,19 @@
  * limitations under the License
  */
 
-package android.content;
+package android.app.task;
 
 import java.util.List;
 
+import android.content.Context;
+
 /**
  * Class for scheduling various types of tasks with the scheduling framework on the device.
  *
- * Get an instance of this class through {@link Context#getSystemService(String)}.
+ * <p>You do not
+ * instantiate this class directly; instead, retrieve it through
+ * {@link android.content.Context#getSystemService
+ * Context.getSystemService(Context.TASK_SERVICE)}.
  */
 public abstract class TaskManager {
     /*
@@ -29,18 +34,20 @@
      * if the run-time for your task is too short, or perhaps the system can't resolve the
      * requisite {@link TaskService} in your package.
      */
-    static final int RESULT_INVALID_PARAMETERS = -1;
+    public static final int RESULT_INVALID_PARAMETERS = -1;
+
     /**
      * Returned from {@link #schedule(Task)} if this application has made too many requests for
      * work over too short a time.
      */
     // TODO: Determine if this is necessary.
-    static final int RESULT_OVER_QUOTA = -2;
+    public static final int RESULT_OVER_QUOTA = -2;
 
-    /*
-     * @param task The task you wish scheduled. See {@link Task#TaskBuilder} for more detail on
-     * the sorts of tasks you can schedule.
-     * @return If >0, this int corresponds to the taskId of the successfully scheduled task.
+    /**
+     * @param task The task you wish scheduled. See
+     * {@link android.app.task.Task.Builder Task.Builder} for more detail on the sorts of tasks
+     * you can schedule.
+     * @return If >0, this int returns the taskId of the successfully scheduled task.
      * Otherwise you have to compare the return value to the error codes defined in this class.
      */
     public abstract int schedule(Task task);
diff --git a/core/java/android/app/task/TaskParams.java b/core/java/android/app/task/TaskParams.java
index 0351082..dacb3480 100644
--- a/core/java/android/app/task/TaskParams.java
+++ b/core/java/android/app/task/TaskParams.java
@@ -47,7 +47,7 @@
 
     /**
      * @return The extras you passed in when constructing this task with
-     * {@link android.content.Task.Builder#setExtras(android.os.Bundle)}. This will
+     * {@link android.app.task.Task.Builder#setExtras(android.os.Bundle)}. This will
      * never be null. If you did not set any extras this will be an empty bundle.
      */
     public Bundle getExtras() {
diff --git a/core/java/android/app/task/TaskService.java b/core/java/android/app/task/TaskService.java
index ab1a565..8ce4484 100644
--- a/core/java/android/app/task/TaskService.java
+++ b/core/java/android/app/task/TaskService.java
@@ -28,7 +28,7 @@
 import com.android.internal.annotations.GuardedBy;
 
 /**
- * <p>Entry point for the callback from the {@link android.content.TaskManager}.</p>
+ * <p>Entry point for the callback from the {@link android.app.task.TaskManager}.</p>
  * <p>This is the base class that handles asynchronous requests that were previously scheduled. You
  * are responsible for overriding {@link TaskService#onStartTask(TaskParams)}, which is where
  * you will implement your task logic.</p>
@@ -215,9 +215,9 @@
      *
      * <p>This will happen if the requirements specified at schedule time are no longer met. For
      * example you may have requested WiFi with
-     * {@link android.content.Task.Builder#setRequiredNetworkCapabilities(int)}, yet while your
+     * {@link android.app.task.Task.Builder#setRequiredNetworkCapabilities(int)}, yet while your
      * task was executing the user toggled WiFi. Another example is if you had specified
-     * {@link android.content.Task.Builder#setRequiresDeviceIdle(boolean)}, and the phone left its
+     * {@link android.app.task.Task.Builder#setRequiresDeviceIdle(boolean)}, and the phone left its
      * idle maintenance window. You are solely responsible for the behaviour of your application
      * upon receipt of this message; your app will likely start to misbehave if you ignore it. One
      * immediate repercussion is that the system will cease holding a wakelock for you.</p>
@@ -237,7 +237,7 @@
      *     You can specify post-execution behaviour to the scheduler here with
      *     <code>needsReschedule </code>. This will apply a back-off timer to your task based on
      *     the default, or what was set with
-     *     {@link android.content.Task.Builder#setBackoffCriteria(long, int)}. The original
+     *     {@link android.app.task.Task.Builder#setBackoffCriteria(long, int)}. The original
      *     requirements are always honoured even for a backed-off task. Note that a task running in
      *     idle mode will not be backed-off. Instead what will happen is the task will be re-added
      *     to the queue and re-executed within a future idle maintenance window.
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 6ae006c..b0673b5 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -2023,6 +2023,7 @@
             PRINT_SERVICE,
             MEDIA_SESSION_SERVICE,
             BATTERY_SERVICE,
+            TASK_SERVICE,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface ServiceName {}
@@ -2079,6 +2080,8 @@
      * <dd> A {@link android.app.DownloadManager} for requesting HTTP downloads
      * <dt> {@link #BATTERY_SERVICE} ("batterymanager")
      * <dd> A {@link android.os.BatteryManager} for managing battery state
+     * <dt> {@link #TASK_SERVICE} ("taskmanager")
+     * <dd>  A {@link android.app.task.TaskManager} for managing scheduled tasks
      * </dl>
      *
      * <p>Note:  System services obtained via this API may be closely associated with
@@ -2134,6 +2137,8 @@
      * @see android.app.DownloadManager
      * @see #BATTERY_SERVICE
      * @see android.os.BatteryManager
+     * @see #TASK_SERVICE
+     * @see android.app.task.TaskManager
      */
     public abstract Object getSystemService(@ServiceName @NonNull String name);
 
@@ -2728,6 +2733,15 @@
     public static final String USAGE_STATS_SERVICE = "usagestats";
 
     /**
+     * Use with {@link #getSystemService} to retrieve a {@link
+     * android.app.task.TaskManager} instance for managing occasional
+     * background tasks.
+     * @see #getSystemService
+     * @see android.app.task.TaskManager
+     */
+    public static final String TASK_SERVICE = "task";
+
+    /**
      * Determine whether the given permission is allowed for a particular
      * process and user ID running in the system.
      *
diff --git a/core/java/android/tv/TvInputInfo.java b/core/java/android/tv/TvInputInfo.java
index 50462cc..217e4b7 100644
--- a/core/java/android/tv/TvInputInfo.java
+++ b/core/java/android/tv/TvInputInfo.java
@@ -39,7 +39,7 @@
     public TvInputInfo(ResolveInfo service) {
         mService = service;
         ServiceInfo si = service.serviceInfo;
-        mId = generateInputIdForComponenetName(new ComponentName(si.packageName, si.name));
+        mId = generateInputIdForComponentName(new ComponentName(si.packageName, si.name));
     }
 
     /**
@@ -134,7 +134,7 @@
      * @return the generated input id for the given {@code name}.
      * @hide
      */
-    public static final String generateInputIdForComponenetName(ComponentName name) {
+    public static final String generateInputIdForComponentName(ComponentName name) {
         return name.flattenToShortString();
     }
 
diff --git a/core/java/android/tv/TvInputService.java b/core/java/android/tv/TvInputService.java
index eeb738d..cb0142f 100644
--- a/core/java/android/tv/TvInputService.java
+++ b/core/java/android/tv/TvInputService.java
@@ -69,7 +69,7 @@
     @Override
     public void onCreate() {
         super.onCreate();
-        mId = TvInputInfo.generateInputIdForComponenetName(
+        mId = TvInputInfo.generateInputIdForComponentName(
                 new ComponentName(getPackageName(), getClass().getName()));
     }
 
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index 8417887..9c9a939 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -33,8 +33,6 @@
 /**
  * Hardware renderer that proxies the rendering to a render thread. Most calls
  * are currently synchronous.
- * TODO: Make draw() async.
- * TODO: Figure out how to share the DisplayList between two threads (global lock?)
  *
  * The UI thread can block on the RenderThread, but RenderThread must never
  * block on the UI thread.
@@ -117,7 +115,7 @@
     @Override
     void destroyHardwareResources(View view) {
         destroyResources(view);
-        // TODO: GLES20Canvas.flushCaches(GLES20Canvas.FLUSH_CACHES_LAYERS);
+        nFlushCaches(mNativeProxy, GLES20Canvas.FLUSH_CACHES_LAYERS);
     }
 
     private static void destroyResources(View view) {
@@ -368,6 +366,8 @@
     private static native boolean nCopyLayerInto(long nativeProxy, long layer, long bitmap);
     private static native void nDestroyLayer(long nativeProxy, long layer);
 
+    private static native void nFlushCaches(long nativeProxy, int flushMode);
+
     private static native void nFence(long nativeProxy);
     private static native void nNotifyFramePending(long nativeProxy);
 }
diff --git a/core/java/android/view/ViewPropertyAnimator.java b/core/java/android/view/ViewPropertyAnimator.java
index 3104862..b92b983 100644
--- a/core/java/android/view/ViewPropertyAnimator.java
+++ b/core/java/android/view/ViewPropertyAnimator.java
@@ -253,9 +253,10 @@
     ViewPropertyAnimator(View view) {
         mView = view;
         view.ensureTransformationInfo();
-        if (view.getContext().getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.L) {
-            mRTBackend = new ViewPropertyAnimatorRT(view);
-        }
+        // TODO: Disabled because of b/15287046
+        //if (view.getContext().getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.L) {
+        //    mRTBackend = new ViewPropertyAnimatorRT(view);
+        //}
     }
 
     /**
diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp
index 5bc0f62..6f256f0 100644
--- a/core/jni/android_view_ThreadedRenderer.cpp
+++ b/core/jni/android_view_ThreadedRenderer.cpp
@@ -293,6 +293,12 @@
     proxy->destroyLayer(layer);
 }
 
+static void android_view_ThreadedRenderer_flushCaches(JNIEnv* env, jobject clazz,
+        jlong proxyPtr, jint flushMode) {
+    RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
+    proxy->flushCaches(static_cast<Caches::FlushMode>(flushMode));
+}
+
 static void android_view_ThreadedRenderer_fence(JNIEnv* env, jobject clazz,
         jlong proxyPtr) {
     RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
@@ -334,6 +340,7 @@
     { "nCreateTextureLayer", "(J)J", (void*) android_view_ThreadedRenderer_createTextureLayer },
     { "nCopyLayerInto", "(JJJ)Z", (void*) android_view_ThreadedRenderer_copyLayerInto },
     { "nDestroyLayer", "(JJ)V", (void*) android_view_ThreadedRenderer_destroyLayer },
+    { "nFlushCaches", "(JI)V", (void*) android_view_ThreadedRenderer_flushCaches },
     { "nFence", "(J)V", (void*) android_view_ThreadedRenderer_fence },
     { "nNotifyFramePending", "(J)V", (void*) android_view_ThreadedRenderer_notifyFramePending },
 #endif
diff --git a/graphics/java/android/graphics/FontFamily.java b/graphics/java/android/graphics/FontFamily.java
index a759a79..210ea86b 100644
--- a/graphics/java/android/graphics/FontFamily.java
+++ b/graphics/java/android/graphics/FontFamily.java
@@ -31,7 +31,6 @@
 
     public FontFamily() {
         mNativePtr = nCreateFamily();
-        mNativePtr = nCreateFamily();
         if (mNativePtr == 0) {
             throw new RuntimeException();
         }
diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java
index 64451c4..b7613fb 100644
--- a/graphics/java/android/graphics/Typeface.java
+++ b/graphics/java/android/graphics/Typeface.java
@@ -66,6 +66,9 @@
     static Map<String, Typeface> sSystemFontMap;
     static FontFamily[] sFallbackFonts;
 
+    static final String SYSTEM_FONTS_CONFIG = "system_fonts.xml";
+    static final String FALLBACK_FONTS_CONFIG = "fallback_fonts.xml";
+
     /**
      * @hide
      */
@@ -249,10 +252,16 @@
         return fontFamily;
     }
 
-    static {
+    /*
+     * (non-Javadoc)
+     *
+     * This should only be called once, from the static class initializer block.
+     */
+    private static void init() {
         // Load font config and initialize Minikin state
-        String systemConfigFilename = "/system/etc/system_fonts.xml";
-        String configFilename = "/system/etc/fallback_fonts.xml";
+        File systemFontConfigLocation = getSystemFontConfigLocation();
+        File systemConfigFilename = new File(systemFontConfigLocation, SYSTEM_FONTS_CONFIG);
+        File configFilename = new File(systemFontConfigLocation, FALLBACK_FONTS_CONFIG);
         try {
             // TODO: throws an exception non-Minikin builds, to fail early;
             // remove when Minikin-only
@@ -301,7 +310,10 @@
         } catch (XmlPullParserException e) {
             Log.e(TAG, "XML parse exception for " + configFilename);
         }
+    }
 
+    static {
+        init();
         // Set up defaults and typefaces exposed in public API
         DEFAULT         = create((String) null, 0);
         DEFAULT_BOLD    = create((String) null, Typeface.BOLD);
@@ -315,6 +327,11 @@
             create((String) null, Typeface.ITALIC),
             create((String) null, Typeface.BOLD_ITALIC),
         };
+
+    }
+
+    private static File getSystemFontConfigLocation() {
+        return new File("/system/etc/");
     }
 
     @Override
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index b6b3428..160fbea 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -556,6 +556,13 @@
     return LayerRenderer::copyLayer(layer->backingLayer(), bitmap);
 }
 
+void CanvasContext::flushCaches(Caches::FlushMode flushMode) {
+    if (mGlobalContext->hasContext()) {
+        requireGlContext();
+        Caches::getInstance().flush(flushMode);
+    }
+}
+
 void CanvasContext::runWithGlContext(RenderTask* task) {
     requireGlContext();
     task->run();
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index a54b33e..da85d448 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -63,6 +63,8 @@
 
     bool copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap);
 
+    void flushCaches(Caches::FlushMode flushMode);
+
     void invokeFunctor(Functor* functor);
 
     void runWithGlContext(RenderTask* task);
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index 2e103d8..8e772f2 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -282,6 +282,18 @@
     post(task);
 }
 
+CREATE_BRIDGE2(flushCaches, CanvasContext* context, Caches::FlushMode flushMode) {
+    args->context->flushCaches(args->flushMode);
+    return NULL;
+}
+
+void RenderProxy::flushCaches(Caches::FlushMode flushMode) {
+    SETUP_TASK(flushCaches);
+    args->context = mContext;
+    args->flushMode = flushMode;
+    post(task);
+}
+
 CREATE_BRIDGE0(fence) {
     // Intentionally empty
     return NULL;
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index 8aeb264..22d4e22 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -29,6 +29,7 @@
 #include <utils/StrongPointer.h>
 #include <utils/Vector.h>
 
+#include "../Caches.h"
 #include "DrawFrameTask.h"
 
 namespace android {
@@ -81,6 +82,8 @@
     ANDROID_API bool copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap);
     ANDROID_API void destroyLayer(DeferredLayerUpdater* layer);
 
+    ANDROID_API void flushCaches(Caches::FlushMode flushMode);
+
     ANDROID_API void fence();
     ANDROID_API void notifyFramePending();
 
diff --git a/media/java/android/media/AudioAttributes.java b/media/java/android/media/AudioAttributes.java
index bb23a36..5a3aaab 100644
--- a/media/java/android/media/AudioAttributes.java
+++ b/media/java/android/media/AudioAttributes.java
@@ -26,6 +26,7 @@
 import java.util.Set;
 
 /**
+ * @hide
  * A class to encapsulate a collection of attributes describing information about an audio
  * player or recorder.
  */
diff --git a/packages/Keyguard/res/layout/keyguard_pin_view.xml b/packages/Keyguard/res/layout/keyguard_pin_view.xml
index a804c8c..a8e330b 100644
--- a/packages/Keyguard/res/layout/keyguard_pin_view.xml
+++ b/packages/Keyguard/res/layout/keyguard_pin_view.xml
@@ -41,28 +41,16 @@
        android:layout_weight="1"
        android:layoutDirection="ltr"
        >
-       <LinearLayout
+       <RelativeLayout
+          android:id="@+id/row0"
           android:layout_width="match_parent"
           android:layout_height="0dp"
-          android:orientation="horizontal"
           android:layout_weight="1"
           >
-          <TextView android:id="@+id/pinEntry"
-               android:editable="true"
-               android:layout_width="0dip"
-               android:layout_height="match_parent"
-               android:layout_weight="1"
-               android:gravity="center"
-               android:layout_marginStart="@dimen/keyguard_lockscreen_pin_margin_left"
-               android:singleLine="true"
-               android:cursorVisible="false"
-               android:background="@null"
-               android:textAppearance="@style/TextAppearance.NumPadKey"
-               android:imeOptions="flagForceAscii|actionDone"
-               />
-           <ImageButton android:id="@+id/delete_button"
+          <ImageButton android:id="@+id/delete_button"
                android:layout_width="wrap_content"
                android:layout_height="match_parent"
+               android:layout_alignParentEnd="true"
                android:gravity="center_vertical"
                android:src="@drawable/ic_input_delete"
                android:clickable="true"
@@ -73,13 +61,30 @@
                android:background="?android:attr/selectableItemBackground"
                android:contentDescription="@string/keyboardview_keycode_delete"
                />
-       </LinearLayout>
-       <View
-           android:layout_width="wrap_content"
-           android:layout_height="1dp"
-           android:background="#55FFFFFF"
-           />
+           <TextView android:id="@+id/pinEntry"
+               android:editable="true"
+               android:layout_width="match_parent"
+               android:layout_height="match_parent"
+               android:layout_toStartOf="@+id/delete_button"
+               android:layout_alignParentStart="true"
+               android:gravity="center"
+               android:layout_marginStart="@dimen/keyguard_lockscreen_pin_margin_left"
+               android:singleLine="true"
+               android:cursorVisible="false"
+               android:background="@null"
+               android:textAppearance="@style/TextAppearance.NumPadKey"
+               android:imeOptions="flagForceAscii|actionDone"
+               />
+            <View
+               android:id="@+id/divider"
+               android:layout_width="match_parent"
+               android:layout_height="1dp"
+               android:layout_alignParentBottom="true"
+               android:background="#55FFFFFF"
+               />
+       </RelativeLayout>
        <LinearLayout
+           android:id="@+id/row1"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1"
@@ -114,6 +119,7 @@
                />
        </LinearLayout>
        <LinearLayout
+           android:id="@+id/row2"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1"
@@ -148,6 +154,7 @@
                />
        </LinearLayout>
        <LinearLayout
+           android:id="@+id/row3"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:orientation="horizontal"
@@ -182,6 +189,7 @@
                />
        </LinearLayout>
        <LinearLayout
+           android:id="@+id/row4"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1"
diff --git a/packages/Keyguard/res/values/dimens.xml b/packages/Keyguard/res/values/dimens.xml
index 3830df7..01d9ab3 100644
--- a/packages/Keyguard/res/values/dimens.xml
+++ b/packages/Keyguard/res/values/dimens.xml
@@ -161,4 +161,6 @@
     <dimen name="widget_big_font_size">68dp</dimen>
     <dimen name="big_font_size">120dp</dimen>
 
+    <!-- The y translation to apply at the start in appear animations. -->
+    <dimen name="appear_y_translation_start">24dp</dimen>
 </resources>
diff --git a/packages/Keyguard/src/com/android/keyguard/AppearAnimationUtils.java b/packages/Keyguard/src/com/android/keyguard/AppearAnimationUtils.java
new file mode 100644
index 0000000..ea896d5
--- /dev/null
+++ b/packages/Keyguard/src/com/android/keyguard/AppearAnimationUtils.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.keyguard;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.TimeInterpolator;
+import android.content.Context;
+import android.view.View;
+import android.view.ViewPropertyAnimator;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
+
+/**
+ * A class to make nice appear transitions for views in a tabular layout.
+ */
+public class AppearAnimationUtils {
+
+    public static final long APPEAR_DURATION = 220;
+
+    private final Interpolator mLinearOutSlowIn;
+    private final float mStartTranslation;
+
+    public AppearAnimationUtils(Context ctx) {
+        mLinearOutSlowIn = AnimationUtils.loadInterpolator(
+                ctx, android.R.interpolator.linear_out_slow_in);
+        mStartTranslation =
+                ctx.getResources().getDimensionPixelOffset(R.dimen.appear_y_translation_start);
+    }
+
+    public void startAppearAnimation(View[][] views, final Runnable finishListener) {
+        long maxDelay = 0;
+        ViewPropertyAnimator maxDelayAnimator = null;
+        for (int row = 0; row < views.length; row++) {
+            View[] columns = views[row];
+            for (int col = 0; col < columns.length; col++) {
+                long delay = calculateDelay(row, col);
+                ViewPropertyAnimator animator = startAppearAnimation(columns[col], delay);
+                if (animator != null && delay > maxDelay) {
+                    maxDelay = delay;
+                    maxDelayAnimator = animator;
+                }
+            }
+        }
+        if (maxDelayAnimator != null) {
+            maxDelayAnimator.setListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    finishListener.run();
+                }
+            });
+        } else {
+            finishListener.run();
+        }
+    }
+
+    private ViewPropertyAnimator startAppearAnimation(View view, long delay) {
+        if (view == null) return null;
+        view.setAlpha(0f);
+        view.setTranslationY(mStartTranslation);
+        view.animate()
+                .alpha(1f)
+                .translationY(0)
+                .setInterpolator(mLinearOutSlowIn)
+                .setDuration(APPEAR_DURATION)
+                .setStartDelay(delay)
+                .setListener(null);
+        if (view.hasOverlappingRendering()) {
+            view.animate().withLayer();
+        }
+        return view.animate();
+    }
+
+    private long calculateDelay(int row, int col) {
+        return (long) (row * 40 + col * (Math.pow(row, 0.4) + 0.4) * 20);
+    }
+
+    public TimeInterpolator getInterpolator() {
+        return mLinearOutSlowIn;
+    }
+
+    public float getStartTranslation() {
+        return mStartTranslation;
+    }
+}
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardPINView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardPINView.java
index 4dfda91..1f3c176 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardPINView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardPINView.java
@@ -22,6 +22,7 @@
 import android.text.method.DigitsKeyListener;
 import android.util.AttributeSet;
 import android.view.View;
+import android.view.ViewGroup;
 import android.widget.TextView.OnEditorActionListener;
 
 /**
@@ -30,12 +31,21 @@
 public class KeyguardPINView extends KeyguardAbsKeyInputView
         implements KeyguardSecurityView, OnEditorActionListener, TextWatcher {
 
+    private final AppearAnimationUtils mAppearAnimationUtils;
+    private ViewGroup mKeyguardBouncerFrame;
+    private ViewGroup mRow0;
+    private ViewGroup mRow1;
+    private ViewGroup mRow2;
+    private ViewGroup mRow3;
+    private View mDivider;
+
     public KeyguardPINView(Context context) {
         this(context, null);
     }
 
     public KeyguardPINView(Context context, AttributeSet attrs) {
         super(context, attrs);
+        mAppearAnimationUtils = new AppearAnimationUtils(context);
     }
 
     protected void resetState() {
@@ -56,6 +66,12 @@
     protected void onFinishInflate() {
         super.onFinishInflate();
 
+        mKeyguardBouncerFrame = (ViewGroup) findViewById(R.id.keyguard_bouncer_frame);
+        mRow0 = (ViewGroup) findViewById(R.id.row0);
+        mRow1 = (ViewGroup) findViewById(R.id.row1);
+        mRow2 = (ViewGroup) findViewById(R.id.row2);
+        mRow3 = (ViewGroup) findViewById(R.id.row3);
+        mDivider = findViewById(R.id.divider);
         final View ok = findViewById(R.id.key_enter);
         if (ok != null) {
             ok.setOnClickListener(new View.OnClickListener() {
@@ -117,8 +133,45 @@
 
     @Override
     public void startAppearAnimation() {
-        // TODO: Fancy animation.
-        setAlpha(0);
-        animate().alpha(1).withLayer().setDuration(200);
+        enableClipping(false);
+        setTranslationY(mAppearAnimationUtils.getStartTranslation());
+        animate()
+                .setDuration(500)
+                .setInterpolator(mAppearAnimationUtils.getInterpolator())
+                .translationY(0);
+        mAppearAnimationUtils.startAppearAnimation(new View[][] {
+                new View[] {
+                        mRow0, null, null
+                },
+                new View[] {
+                        findViewById(R.id.key1), findViewById(R.id.key2), findViewById(R.id.key3)
+                },
+                new View[] {
+                        findViewById(R.id.key4), findViewById(R.id.key5), findViewById(R.id.key6)
+                },
+                new View[] {
+                        findViewById(R.id.key7), findViewById(R.id.key8), findViewById(R.id.key9)
+                },
+                new View[] {
+                        null, findViewById(R.id.key0), findViewById(R.id.key_enter)
+                },
+                new View[] {
+                        null, mEcaView, null
+                }},
+                new Runnable() {
+                    @Override
+                    public void run() {
+                        enableClipping(true);
+                    }
+                });
+    }
+
+    private void enableClipping(boolean enable) {
+        mKeyguardBouncerFrame.setClipToPadding(enable);
+        mKeyguardBouncerFrame.setClipChildren(enable);
+        mRow1.setClipToPadding(enable);
+        mRow2.setClipToPadding(enable);
+        mRow3.setClipToPadding(enable);
+        setClipChildren(enable);
     }
 }
diff --git a/packages/SystemUI/res/layout/keyguard_bottom_area.xml b/packages/SystemUI/res/layout/keyguard_bottom_area.xml
index 936f73b..9bf42b2 100644
--- a/packages/SystemUI/res/layout/keyguard_bottom_area.xml
+++ b/packages/SystemUI/res/layout/keyguard_bottom_area.xml
@@ -22,7 +22,7 @@
     android:layout_height="match_parent"
     android:layout_width="match_parent"
     >
-    <com.android.systemui.statusbar.phone.SwipeAffordanceView
+    <com.android.systemui.statusbar.AlphaImageView
         android:id="@+id/camera_button"
         android:layout_height="64dp"
         android:layout_width="64dp"
@@ -30,10 +30,9 @@
         android:tint="#ffffffff"
         android:src="@drawable/ic_camera_alt_24dp"
         android:scaleType="center"
-        android:contentDescription="@string/accessibility_camera_button"
-        systemui:swipeDirection="start"/>
+        android:contentDescription="@string/accessibility_camera_button" />
 
-    <com.android.systemui.statusbar.phone.SwipeAffordanceView
+    <com.android.systemui.statusbar.AlphaImageView
         android:id="@+id/phone_button"
         android:layout_height="64dp"
         android:layout_width="64dp"
@@ -41,8 +40,7 @@
         android:tint="#ffffffff"
         android:src="@drawable/ic_phone_24dp"
         android:scaleType="center"
-        android:contentDescription="@string/accessibility_phone_button"
-        systemui:swipeDirection="end"/>
+        android:contentDescription="@string/accessibility_phone_button" />
 
     <com.android.systemui.statusbar.phone.KeyguardIndicationTextView
         android:id="@+id/keyguard_indication_text"
@@ -54,15 +52,13 @@
         android:textColor="#ffffff"
         android:textAppearance="?android:attr/textAppearanceSmall"/>
 
-    <ImageView
+    <com.android.systemui.statusbar.AlphaImageView
         android:id="@+id/lock_icon"
         android:layout_width="64dp"
         android:layout_height="64dp"
         android:layout_gravity="bottom|center_horizontal"
         android:src="@drawable/ic_lock_24dp"
         android:scaleType="center"
-        android:alpha="0.7"
-        android:layerType="hardware"
-        android:tint="#ffffffff"/>
+        android:tint="#ffffffff" />
 
 </com.android.systemui.statusbar.phone.KeyguardBottomAreaView>
diff --git a/packages/SystemUI/res/values/attrs.xml b/packages/SystemUI/res/values/attrs.xml
index 3549689..c453618 100644
--- a/packages/SystemUI/res/values/attrs.xml
+++ b/packages/SystemUI/res/values/attrs.xml
@@ -43,12 +43,6 @@
     <declare-styleable name="BatteryMeterView">
         <attr name="frameColor" format="color" />
     </declare-styleable>
-    <declare-styleable name="SwipeAffordanceView">
-        <attr name="swipeDirection" format="enum">
-            <enum name="start" value="0" />
-            <enum name="end" value="1" />
-        </attr>
-    </declare-styleable>
     <declare-styleable name="Clock">
         <attr name="amPmStyle" format="enum">
             <enum name="normal" value="0" />
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 6e35230..610b376 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -294,6 +294,9 @@
     <dimen name="keyguard_clock_notifications_margin_min">22dp</dimen>
     <dimen name="keyguard_clock_notifications_margin_max">36dp</dimen>
 
+    <!-- The minimum amount the user needs to swipe to go to the camera / phone. -->
+    <dimen name="keyguard_min_swipe_amount">75dp</dimen>
+
     <!-- Volume panel dialog y offset -->
     <dimen name="volume_panel_top">16dp</dimen>
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/AlphaImageView.java b/packages/SystemUI/src/com/android/systemui/statusbar/AlphaImageView.java
new file mode 100644
index 0000000..06dc4e6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/AlphaImageView.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.widget.ImageView;
+
+/**
+ * An ImageView which does not have overlapping renderings commands and therefore does not need a
+ * layer when alpha is changed.
+ */
+public class AlphaImageView extends ImageView {
+    public AlphaImageView(Context context) {
+        super(context);
+    }
+
+    public AlphaImageView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public AlphaImageView(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+    }
+
+    public AlphaImageView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+    }
+
+    @Override
+    public boolean hasOverlappingRendering() {
+        return false;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index f4db625..6246e05 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -48,6 +48,7 @@
 import android.service.dreams.DreamService;
 import android.service.dreams.IDreamManager;
 import android.service.notification.NotificationListenerService;
+import android.service.notification.NotificationListenerService.Ranking;
 import android.service.notification.StatusBarNotification;
 import android.text.TextUtils;
 import android.util.Log;
@@ -77,11 +78,12 @@
 import com.android.systemui.RecentsComponent;
 import com.android.systemui.SearchPanelView;
 import com.android.systemui.SystemUI;
+import com.android.systemui.statusbar.NotificationData.Entry;
 import com.android.systemui.statusbar.phone.KeyguardTouchDelegate;
 import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
 
 import java.util.ArrayList;
-import java.util.Arrays;
+import java.util.Collections;
 import java.util.Locale;
 
 import static com.android.keyguard.KeyguardHostView.OnDismissAction;
@@ -194,7 +196,7 @@
                     mContext.getContentResolver(), Settings.Global.DEVICE_PROVISIONED, 0);
             if (provisioned != mDeviceProvisioned) {
                 mDeviceProvisioned = provisioned;
-                updateNotificationIcons();
+                updateNotifications();
             }
             final int mode = Settings.Global.getInt(mContext.getContentResolver(),
                     Settings.Global.ZEN_MODE, Settings.Global.ZEN_MODE_OFF);
@@ -209,7 +211,7 @@
             // so we just dump our cache ...
             mUsersAllowingPrivateNotifications.clear();
             // ... and refresh all the notifications
-            updateNotificationIcons();
+            updateNotifications();
         }
     };
 
@@ -280,11 +282,12 @@
         public void onListenerConnected() {
             if (DEBUG) Log.d(TAG, "onListenerConnected");
             final StatusBarNotification[] notifications = getActiveNotifications();
+            final Ranking currentRanking = getCurrentRanking();
             mHandler.post(new Runnable() {
                 @Override
                 public void run() {
                     for (StatusBarNotification sbn : notifications) {
-                        addNotificationInternal(sbn);
+                        addNotificationInternal(sbn, currentRanking);
                     }
                 }
             });
@@ -293,13 +296,14 @@
         @Override
         public void onNotificationPosted(final StatusBarNotification sbn) {
             if (DEBUG) Log.d(TAG, "onNotificationPosted: " + sbn);
+            final Ranking currentRanking = getCurrentRanking();
             mHandler.post(new Runnable() {
                 @Override
                 public void run() {
                     if (mNotificationData.findByKey(sbn.getKey()) != null) {
-                        updateNotificationInternal(sbn);
+                        updateNotificationInternal(sbn, currentRanking);
                     } else {
-                        addNotificationInternal(sbn);
+                        addNotificationInternal(sbn, currentRanking);
                     }
                 }
             });
@@ -308,10 +312,24 @@
         @Override
         public void onNotificationRemoved(final StatusBarNotification sbn) {
             if (DEBUG) Log.d(TAG, "onNotificationRemoved: " + sbn);
+            final Ranking currentRanking = getCurrentRanking();
             mHandler.post(new Runnable() {
                 @Override
                 public void run() {
-                    removeNotificationInternal(sbn.getKey());
+                    removeNotificationInternal(sbn.getKey(), currentRanking);
+                }
+            });
+        }
+
+        @Override
+        public void onNotificationRankingUpdate() {
+            if (DEBUG) Log.d(TAG, "onRankingUpdate");
+            final Ranking currentRanking = getCurrentRanking();
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    mNotificationData.updateRanking(currentRanking);
+                    updateNotifications();
                 }
             });
         }
@@ -1113,19 +1131,13 @@
         }
     }
 
-    protected StatusBarNotification removeNotificationViews(String key) {
-        NotificationData.Entry entry = mNotificationData.remove(key);
+    protected StatusBarNotification removeNotificationViews(String key, Ranking ranking) {
+        NotificationData.Entry entry = mNotificationData.remove(key, ranking);
         if (entry == null) {
             Log.w(TAG, "removeNotification for unknown key: " + key);
             return null;
         }
-        // Remove the expanded view.
-        ViewGroup rowParent = (ViewGroup)entry.row.getParent();
-        if (rowParent != null) rowParent.removeView(entry.row);
-        updateRowStates();
-        updateNotificationIcons();
-        updateSpeedBump();
-
+        updateNotifications();
         return entry.notification;
     }
 
@@ -1159,35 +1171,17 @@
         return entry;
     }
 
-    protected void addNotificationViews(NotificationData.Entry entry) {
+    protected void addNotificationViews(Entry entry, Ranking ranking) {
         if (entry == null) {
             return;
         }
         // Add the expanded view and icon.
-        int pos = mNotificationData.add(entry);
-        if (DEBUG) {
-            Log.d(TAG, "addNotificationViews: added at " + pos);
-        }
-        updateRowStates();
-        updateNotificationIcons();
-        updateSpeedBump();
+        mNotificationData.add(entry, ranking);
+        updateNotifications();
     }
 
-    protected void updateSpeedBump() {
-        int n = mNotificationData.size();
-        int speedBumpIndex = -1;
-        for (int i = n-1; i >= 0; i--) {
-            NotificationData.Entry entry = mNotificationData.get(i);
-            if (entry.row.getVisibility() != View.GONE && speedBumpIndex == -1
-                    && entry.row.isBelowSpeedBump() ) {
-                speedBumpIndex = n - 1 - i;
-            }
-        }
-        mStackScroller.updateSpeedBumpIndex(speedBumpIndex);
-    }
-
-    private void addNotificationViews(StatusBarNotification notification) {
-        addNotificationViews(createNotificationViews(notification));
+    private void addNotificationViews(StatusBarNotification notification, Ranking ranking) {
+        addNotificationViews(createNotificationViews(notification), ranking);
     }
 
     /**
@@ -1201,17 +1195,17 @@
     protected void updateRowStates() {
         int maxKeyguardNotifications = getMaxKeyguardNotifications();
         mKeyguardIconOverflowContainer.getIconsView().removeAllViews();
-        int n = mNotificationData.size();
+        final int N = mNotificationData.size();
         int visibleNotifications = 0;
         boolean onKeyguard = mState == StatusBarState.KEYGUARD;
-        for (int i = n-1; i >= 0; i--) {
+        for (int i = 0; i < N; i++) {
             NotificationData.Entry entry = mNotificationData.get(i);
             if (onKeyguard) {
                 entry.row.setExpansionDisabled(true);
             } else {
                 entry.row.setExpansionDisabled(false);
                 if (!entry.row.isUserLocked()) {
-                    boolean top = (i == n-1);
+                    boolean top = (i == 0);
                     entry.row.setSystemExpanded(top);
                 }
             }
@@ -1238,6 +1232,9 @@
         } else {
             mKeyguardIconOverflowContainer.setVisibility(View.GONE);
         }
+        // Move overflow container to last position.
+        mStackScroller.changeViewPosition(mKeyguardIconOverflowContainer,
+                mStackScroller.getChildCount() - 1);
     }
 
     private boolean shouldShowOnKeyguard(StatusBarNotification sbn) {
@@ -1247,46 +1244,42 @@
     protected void setZenMode(int mode) {
         if (!isDeviceProvisioned()) return;
         mZenMode = mode;
-        updateNotificationIcons();
+        updateNotifications();
     }
 
     protected abstract void haltTicker();
     protected abstract void setAreThereNotifications();
-    protected abstract void updateNotificationIcons();
+    protected abstract void updateNotifications();
     protected abstract void tick(StatusBarNotification n, boolean firstTime);
     protected abstract void updateExpandedViewPos(int expandedPosition);
     protected abstract boolean shouldDisableNavbarGestures();
 
-    protected boolean isTopNotification(ViewGroup parent, NotificationData.Entry entry) {
-        return parent != null && parent.indexOfChild(entry.row) == 0;
-    }
-
-
     @Override
     public void addNotification(StatusBarNotification notification) {
         if (!USE_NOTIFICATION_LISTENER) {
-            addNotificationInternal(notification);
+            addNotificationInternal(notification, null);
         }
     }
 
-    public abstract void addNotificationInternal(StatusBarNotification notification);
+    public abstract void addNotificationInternal(StatusBarNotification notification,
+            Ranking ranking);
 
     @Override
     public void removeNotification(String key) {
         if (!USE_NOTIFICATION_LISTENER) {
-            removeNotificationInternal(key);
+            removeNotificationInternal(key, null);
         }
     }
 
-    protected abstract void removeNotificationInternal(String key);
+    protected abstract void removeNotificationInternal(String key, Ranking ranking);
 
     public void updateNotification(StatusBarNotification notification) {
         if (!USE_NOTIFICATION_LISTENER) {
-            updateNotificationInternal(notification);
+            updateNotificationInternal(notification, null);
         }
     }
 
-    public void updateNotificationInternal(StatusBarNotification notification) {
+    public void updateNotificationInternal(StatusBarNotification notification, Ranking ranking) {
         if (DEBUG) Log.d(TAG, "updateNotification(" + notification + ")");
 
         final NotificationData.Entry oldEntry = mNotificationData.findByKey(notification.getKey());
@@ -1358,18 +1351,12 @@
                         && oldPublicContentView.getPackage().equals(publicContentView.getPackage())
                         && oldPublicContentView.getLayoutId() == publicContentView.getLayoutId());
 
-        ViewGroup rowParent = (ViewGroup) oldEntry.row.getParent();
-        boolean orderUnchanged =
-                   notification.getNotification().when == oldNotification.getNotification().when
-                && notification.getScore() == oldNotification.getScore();
-        // score now encompasses/supersedes isOngoing()
 
         boolean updateTicker = notification.getNotification().tickerText != null
                 && !TextUtils.equals(notification.getNotification().tickerText,
                 oldEntry.notification.getNotification().tickerText);
-        boolean isTopAnyway = isTopNotification(rowParent, oldEntry);
-        if (contentsUnchanged && bigContentsUnchanged && headsUpContentsUnchanged && publicUnchanged
-                && (orderUnchanged || isTopAnyway)) {
+        if (contentsUnchanged && bigContentsUnchanged && headsUpContentsUnchanged
+                && publicUnchanged) {
             if (DEBUG) Log.d(TAG, "reusing notification for key: " + notification.getKey());
             oldEntry.notification = notification;
             try {
@@ -1397,22 +1384,20 @@
                     handleNotificationError(notification, "Couldn't update icon: " + ic);
                     return;
                 }
-                updateRowStates();
-                updateSpeedBump();
+                mNotificationData.updateRanking(ranking);
+                updateNotifications();
             }
             catch (RuntimeException e) {
                 // It failed to add cleanly.  Log, and remove the view from the panel.
                 Log.w(TAG, "Couldn't reapply views for package " + contentView.getPackage(), e);
-                removeNotificationViews(notification.getKey());
-                addNotificationViews(notification);
+                removeNotificationViews(notification.getKey(), ranking);
+                addNotificationViews(notification, ranking);
             }
         } else {
             if (DEBUG) Log.d(TAG, "not reusing notification for key: " + notification.getKey());
             if (DEBUG) Log.d(TAG, "contents was " + (contentsUnchanged ? "unchanged" : "changed"));
-            if (DEBUG) Log.d(TAG, "order was " + (orderUnchanged ? "unchanged" : "changed"));
-            if (DEBUG) Log.d(TAG, "notification is " + (isTopAnyway ? "top" : "not top"));
-            removeNotificationViews(notification.getKey());
-            addNotificationViews(notification);  // will also replace the heads up
+            removeNotificationViews(notification.getKey(), ranking);
+            addNotificationViews(notification, ranking);  // will also replace the heads up
             final NotificationData.Entry newEntry = mNotificationData.findByKey(
                     notification.getKey());
             final boolean userChangedExpansion = oldEntry.row.hasUserChangedExpansion();
@@ -1559,5 +1544,12 @@
             mWindowManager.removeViewImmediate(mSearchPanelView);
         }
         mContext.unregisterReceiver(mBroadcastReceiver);
+        if (USE_NOTIFICATION_LISTENER) {
+            try {
+                mNotificationListener.unregisterAsSystemService();
+            } catch (RemoteException e) {
+                // Ignore.
+            }
+        }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/FlingAnimationUtils.java b/packages/SystemUI/src/com/android/systemui/statusbar/FlingAnimationUtils.java
index 7d576cb..5f1325b5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/FlingAnimationUtils.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/FlingAnimationUtils.java
@@ -18,6 +18,7 @@
 
 import android.animation.ValueAnimator;
 import android.content.Context;
+import android.view.ViewPropertyAnimator;
 import android.view.animation.AnimationUtils;
 import android.view.animation.Interpolator;
 import android.view.animation.PathInterpolator;
@@ -46,6 +47,8 @@
     private float mMaxLengthSeconds;
     private float mHighVelocityPxPerSecond;
 
+    private AnimatorProperties mAnimatorProperties = new AnimatorProperties();
+
     public FlingAnimationUtils(Context ctx, float maxLengthSeconds) {
         mMaxLengthSeconds = maxLengthSeconds;
         mLinearOutSlowIn = new PathInterpolator(0, 0, LINEAR_OUT_SLOW_IN_X2, 1);
@@ -80,18 +83,59 @@
      * @param currValue the current value
      * @param endValue the end value of the animator
      * @param velocity the current velocity of the motion
+     */
+    public void apply(ViewPropertyAnimator animator, float currValue, float endValue,
+            float velocity) {
+        apply(animator, currValue, endValue, velocity, Math.abs(endValue - currValue));
+    }
+
+    /**
+     * Applies the interpolator and length to the animator, such that the fling animation is
+     * consistent with the finger motion.
+     *
+     * @param animator the animator to apply
+     * @param currValue the current value
+     * @param endValue the end value of the animator
+     * @param velocity the current velocity of the motion
      * @param maxDistance the maximum distance for this interaction; the maximum animation length
      *                    gets multiplied by the ratio between the actual distance and this value
      */
     public void apply(ValueAnimator animator, float currValue, float endValue, float velocity,
             float maxDistance) {
+        AnimatorProperties properties = getProperties(currValue, endValue, velocity,
+                maxDistance);
+        animator.setDuration(properties.duration);
+        animator.setInterpolator(properties.interpolator);
+    }
+
+    /**
+     * Applies the interpolator and length to the animator, such that the fling animation is
+     * consistent with the finger motion.
+     *
+     * @param animator the animator to apply
+     * @param currValue the current value
+     * @param endValue the end value of the animator
+     * @param velocity the current velocity of the motion
+     * @param maxDistance the maximum distance for this interaction; the maximum animation length
+     *                    gets multiplied by the ratio between the actual distance and this value
+     */
+    public void apply(ViewPropertyAnimator animator, float currValue, float endValue,
+            float velocity, float maxDistance) {
+        AnimatorProperties properties = getProperties(currValue, endValue, velocity,
+                maxDistance);
+        animator.setDuration(properties.duration);
+        animator.setInterpolator(properties.interpolator);
+    }
+
+    private AnimatorProperties getProperties(float currValue,
+            float endValue, float velocity, float maxDistance) {
         float maxLengthSeconds = (float) (mMaxLengthSeconds
                 * Math.sqrt(Math.abs(endValue - currValue) / maxDistance));
         float diff = Math.abs(endValue - currValue);
         float velAbs = Math.abs(velocity);
         float durationSeconds = LINEAR_OUT_SLOW_IN_START_GRADIENT * diff / velAbs;
         if (durationSeconds <= maxLengthSeconds) {
-            animator.setInterpolator(mLinearOutSlowIn);
+            mAnimatorProperties.interpolator = mLinearOutSlowIn;
         } else if (velAbs >= mMinVelocityPxPerSecond) {
 
             // Cross fade between fast-out-slow-in and linear interpolator with current velocity.
@@ -100,14 +144,15 @@
                     = new VelocityInterpolator(durationSeconds, velAbs, diff);
             InterpolatorInterpolator superInterpolator = new InterpolatorInterpolator(
                     velocityInterpolator, mLinearOutSlowIn, mLinearOutSlowIn);
-            animator.setInterpolator(superInterpolator);
+            mAnimatorProperties.interpolator = superInterpolator;
         } else {
 
             // Just use a normal interpolator which doesn't take the velocity into account.
             durationSeconds = maxLengthSeconds;
-            animator.setInterpolator(mFastOutSlowIn);
+            mAnimatorProperties.interpolator = mFastOutSlowIn;
         }
-        animator.setDuration((long) (durationSeconds * 1000));
+        mAnimatorProperties.duration = (long) (durationSeconds * 1000);
+        return mAnimatorProperties;
     }
 
     /**
@@ -124,6 +169,34 @@
      */
     public void applyDismissing(ValueAnimator animator, float currValue, float endValue,
             float velocity, float maxDistance) {
+        AnimatorProperties properties = getDismissingProperties(currValue, endValue, velocity,
+                maxDistance);
+        animator.setDuration(properties.duration);
+        animator.setInterpolator(properties.interpolator);
+    }
+
+    /**
+     * Applies the interpolator and length to the animator, such that the fling animation is
+     * consistent with the finger motion for the case when the animation is making something
+     * disappear.
+     *
+     * @param animator the animator to apply
+     * @param currValue the current value
+     * @param endValue the end value of the animator
+     * @param velocity the current velocity of the motion
+     * @param maxDistance the maximum distance for this interaction; the maximum animation length
+     *                    gets multiplied by the ratio between the actual distance and this value
+     */
+    public void applyDismissing(ViewPropertyAnimator animator, float currValue, float endValue,
+            float velocity, float maxDistance) {
+        AnimatorProperties properties = getDismissingProperties(currValue, endValue, velocity,
+                maxDistance);
+        animator.setDuration(properties.duration);
+        animator.setInterpolator(properties.interpolator);
+    }
+
+    private AnimatorProperties getDismissingProperties(float currValue, float endValue,
+            float velocity, float maxDistance) {
         float maxLengthSeconds = (float) (mMaxLengthSeconds
                 * Math.pow(Math.abs(endValue - currValue) / maxDistance, 0.5f));
         float diff = Math.abs(endValue - currValue);
@@ -135,7 +208,7 @@
         Interpolator mLinearOutFasterIn = new PathInterpolator(0, 0, 1, y2);
         float durationSeconds = startGradient * diff / velAbs;
         if (durationSeconds <= maxLengthSeconds) {
-            animator.setInterpolator(mLinearOutFasterIn);
+            mAnimatorProperties.interpolator = mLinearOutFasterIn;
         } else if (velAbs >= mMinVelocityPxPerSecond) {
 
             // Cross fade between linear-out-faster-in and linear interpolator with current
@@ -145,14 +218,15 @@
                     = new VelocityInterpolator(durationSeconds, velAbs, diff);
             InterpolatorInterpolator superInterpolator = new InterpolatorInterpolator(
                     velocityInterpolator, mLinearOutFasterIn, mLinearOutSlowIn);
-            animator.setInterpolator(superInterpolator);
+            mAnimatorProperties.interpolator = superInterpolator;
         } else {
 
             // Just use a normal interpolator which doesn't take the velocity into account.
             durationSeconds = maxLengthSeconds;
-            animator.setInterpolator(mFastOutLinearIn);
+            mAnimatorProperties.interpolator = mFastOutLinearIn;
         }
-        animator.setDuration((long) (durationSeconds * 1000));
+        mAnimatorProperties.duration = (long) (durationSeconds * 1000);
+        return mAnimatorProperties;
     }
 
     /**
@@ -221,4 +295,10 @@
             return time * mVelocity / mDiff;
         }
     }
+
+    private static class AnimatorProperties {
+        Interpolator interpolator;
+        long duration;
+    }
+
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/InterceptedNotifications.java b/packages/SystemUI/src/com/android/systemui/statusbar/InterceptedNotifications.java
index 0555879..993bb92 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/InterceptedNotifications.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/InterceptedNotifications.java
@@ -50,7 +50,7 @@
         for (int i = 0; i < n; i++) {
             final StatusBarNotification sbn = mIntercepted.valueAt(i);
             sbn.getNotification().extras.putBoolean(EXTRA_INTERCEPT, false);
-            mBar.addNotificationInternal(sbn);
+            mBar.addNotificationInternal(sbn, null);
         }
         mIntercepted.clear();
         updateSyntheticNotification();
@@ -88,7 +88,7 @@
     private void updateSyntheticNotification() {
         if (mIntercepted.isEmpty()) {
             if (mSynKey != null) {
-                mBar.removeNotificationInternal(mSynKey);
+                mBar.removeNotificationInternal(mSynKey, null);
                 mSynKey = null;
             }
             return;
@@ -107,9 +107,9 @@
                 mBar.getCurrentUserHandle());
         if (mSynKey == null) {
             mSynKey = sbn.getKey();
-            mBar.addNotificationInternal(sbn);
+            mBar.addNotificationInternal(sbn, null);
         } else {
-           mBar.updateNotificationInternal(sbn);
+           mBar.updateNotificationInternal(sbn, null);
         }
         final NotificationData.Entry entry = mBar.mNotificationData.findByKey(mSynKey);
         entry.row.setOnClickListener(mSynClickListener);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
index 5696246..d829ac0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
@@ -16,15 +16,19 @@
 
 package com.android.systemui.statusbar;
 
+import android.app.Notification;
+import android.service.notification.NotificationListenerService.Ranking;
 import android.service.notification.StatusBarNotification;
 import android.view.View;
-import android.widget.ImageView;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.Comparator;
 
 /**
  * The list of currently displaying notifications.
+ *
+ * TODO: Rename to NotificationList.
  */
 public class NotificationData {
     public static final class Entry {
@@ -34,7 +38,6 @@
         public ExpandableNotificationRow row; // the outer expanded view
         public View expanded; // the inflated RemoteViews
         public View expandedPublic; // for insecure lockscreens
-        public ImageView largeIcon;
         public View expandedBig;
         private boolean interruption;
         public Entry() {}
@@ -64,18 +67,23 @@
     }
 
     private final ArrayList<Entry> mEntries = new ArrayList<Entry>();
-    private final Comparator<Entry> mEntryCmp = new Comparator<Entry>() {
-        // sort first by score, then by when
+    private Ranking mRanking;
+    private final Comparator<Entry> mRankingComparator = new Comparator<Entry>() {
+        @Override
         public int compare(Entry a, Entry b) {
+            if (mRanking != null) {
+                return mRanking.getRank(a.key) - mRanking.getRank(b.key);
+            }
+
             final StatusBarNotification na = a.notification;
             final StatusBarNotification nb = b.notification;
-            int d = na.getScore() - nb.getScore();
+            int d = nb.getScore() - na.getScore();
             if (a.interruption != b.interruption) {
-                return a.interruption ? 1 : -1;
+                return a.interruption ? -1 : 1;
             } else if (d != 0) {
                 return d;
             } else {
-                return (int) (na.getNotification().when - nb.getNotification().when);
+                return (int) (nb.getNotification().when - na.getNotification().when);
             }
         }
     };
@@ -97,26 +105,47 @@
         return null;
     }
 
-    public int add(Entry entry) {
-        int i;
-        int N = mEntries.size();
-        for (i = 0; i < N; i++) {
-            if (mEntryCmp.compare(mEntries.get(i), entry) > 0) {
-                break;
-            }
-        }
-        mEntries.add(i, entry);
-        return i;
+    public void add(Entry entry, Ranking ranking) {
+        mEntries.add(entry);
+        updateRankingAndSort(ranking);
     }
 
-    public Entry remove(String key) {
+    public Entry remove(String key, Ranking ranking) {
         Entry e = findByKey(key);
-        if (e != null) {
-            mEntries.remove(e);
+        if (e == null) {
+            return null;
         }
+        mEntries.remove(e);
+        updateRankingAndSort(ranking);
         return e;
     }
 
+    public void updateRanking(Ranking ranking) {
+        updateRankingAndSort(ranking);
+    }
+
+    public boolean isAmbient(String key) {
+        // TODO: Remove when switching to NotificationListener.
+        if (mRanking == null) {
+            for (Entry entry : mEntries) {
+                if (key.equals(entry.key)) {
+                    return entry.notification.getNotification().priority ==
+                            Notification.PRIORITY_MIN;
+                }
+            }
+        } else {
+            return mRanking.isAmbient(key);
+        }
+        return false;
+    }
+
+    private void updateRankingAndSort(Ranking ranking) {
+        if (ranking != null) {
+            mRanking = ranking;
+        }
+        Collections.sort(mEntries, mRankingComparator);
+    }
+
     /**
      * Return whether there are any visible items (i.e. items without an error).
      */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
index 714ad06..994b329 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
@@ -23,7 +23,6 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.PackageManager;
-import android.os.PowerManager;
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.provider.MediaStore;
@@ -33,26 +32,23 @@
 import android.view.accessibility.AccessibilityManager;
 import android.widget.FrameLayout;
 import android.widget.ImageView;
-
 import com.android.systemui.R;
 
 /**
  * Implementation for the bottom area of the Keyguard, including camera/phone affordance and status
  * text.
  */
-public class KeyguardBottomAreaView extends FrameLayout
-        implements SwipeAffordanceView.AffordanceListener,
+public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickListener,
         UnlockMethodCache.OnUnlockMethodChangedListener {
 
     final static String TAG = "PhoneStatusBar/KeyguardBottomAreaView";
 
     private static final Intent PHONE_INTENT = new Intent(Intent.ACTION_DIAL);
 
-    private SwipeAffordanceView mCameraButton;
-    private SwipeAffordanceView mPhoneButton;
+    private ImageView mCameraImageView;
+    private ImageView mPhoneImageView;
     private ImageView mLockIcon;
 
-    private PowerManager mPowerManager;
     private ActivityStarter mActivityStarter;
     private UnlockMethodCache mUnlockMethodCache;
 
@@ -76,11 +72,9 @@
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
-        mCameraButton = (SwipeAffordanceView) findViewById(R.id.camera_button);
-        mPhoneButton = (SwipeAffordanceView) findViewById(R.id.phone_button);
+        mCameraImageView = (ImageView) findViewById(R.id.camera_button);
+        mPhoneImageView = (ImageView) findViewById(R.id.phone_button);
         mLockIcon = (ImageView) findViewById(R.id.lock_icon);
-        mCameraButton.setAffordanceListener(this);
-        mPhoneButton.setAffordanceListener(this);
         watchForDevicePolicyChanges();
         watchForAccessibilityChanges();
         updateCameraVisibility();
@@ -88,7 +82,6 @@
         mUnlockMethodCache = UnlockMethodCache.getInstance(getContext());
         mUnlockMethodCache.addListener(this);
         updateTrust();
-        mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
     }
 
     public void setActivityStarter(ActivityStarter activityStarter) {
@@ -97,12 +90,12 @@
 
     private void updateCameraVisibility() {
         boolean visible = !isCameraDisabledByDpm();
-        mCameraButton.setVisibility(visible ? View.VISIBLE : View.GONE);
+        mCameraImageView.setVisibility(visible ? View.VISIBLE : View.GONE);
     }
 
     private void updatePhoneVisibility() {
         boolean visible = isPhoneVisible();
-        mPhoneButton.setVisibility(visible ? View.VISIBLE : View.GONE);
+        mPhoneImageView.setVisibility(visible ? View.VISIBLE : View.GONE);
     }
 
     private boolean isPhoneVisible() {
@@ -162,33 +155,31 @@
     }
 
     private void enableAccessibility(boolean touchExplorationEnabled) {
-        mCameraButton.enableAccessibility(touchExplorationEnabled);
-        mPhoneButton.enableAccessibility(touchExplorationEnabled);
+        mCameraImageView.setOnClickListener(touchExplorationEnabled ? this : null);
+        mCameraImageView.setClickable(touchExplorationEnabled);
+        mPhoneImageView.setOnClickListener(touchExplorationEnabled ? this : null);
+        mPhoneImageView.setClickable(touchExplorationEnabled);
     }
 
-    private void launchCamera() {
+    @Override
+    public void onClick(View v) {
+        if (v == mCameraImageView) {
+            launchCamera();
+        } else if (v == mPhoneImageView) {
+            launchPhone();
+        }
+    }
+
+    public void launchCamera() {
         mContext.startActivityAsUser(
                 new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE),
                 UserHandle.CURRENT);
     }
 
-    private void launchPhone() {
+    public void launchPhone() {
         mActivityStarter.startActivity(PHONE_INTENT);
     }
 
-    @Override
-    public void onUserActivity(long when) {
-        mPowerManager.userActivity(when, false);
-    }
-
-    @Override
-    public void onActionPerformed(SwipeAffordanceView view) {
-        if (view == mCameraButton) {
-            launchCamera();
-        } else if (view == mPhoneButton) {
-            launchPhone();
-        }
-    }
 
     @Override
     protected void onVisibilityChanged(View changedView, int visibility) {
@@ -208,6 +199,18 @@
         mLockIcon.setImageResource(iconRes);
     }
 
+    public ImageView getPhoneImageView() {
+        return mPhoneImageView;
+    }
+
+    public ImageView getCameraImageView() {
+        return mCameraImageView;
+    }
+
+    public ImageView getLockIcon() {
+        return mLockIcon;
+    }
+
     @Override
     public void onMethodSecureChanged(boolean methodSecure) {
         updateTrust();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardPageSwipeHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardPageSwipeHelper.java
new file mode 100644
index 0000000..b4f4865
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardPageSwipeHelper.java
@@ -0,0 +1,398 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar.phone;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
+import android.content.Context;
+import android.os.PowerManager;
+import android.view.MotionEvent;
+import android.view.VelocityTracker;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.view.ViewPropertyAnimator;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
+import com.android.systemui.R;
+import com.android.systemui.statusbar.FlingAnimationUtils;
+
+import java.util.ArrayList;
+
+/**
+ * A touch handler of the Keyguard which is responsible for swiping the content left or right.
+ */
+public class KeyguardPageSwipeHelper {
+
+    private static final float SWIPE_MAX_ICON_SCALE_AMOUNT = 2.0f;
+    private static final float SWIPE_RESTING_ALPHA_AMOUNT = 0.7f;
+    private final Context mContext;
+
+    private FlingAnimationUtils mFlingAnimationUtils;
+    private Callback mCallback;
+    private int mTrackingPointer;
+    private VelocityTracker mVelocityTracker;
+    private boolean mSwipingInProgress;
+    private float mInitialTouchX;
+    private float mInitialTouchY;
+    private float mTranslation;
+    private float mTranslationOnDown;
+    private int mTouchSlop;
+    private int mMinTranslationAmount;
+    private int mMinFlingVelocity;
+    private PowerManager mPowerManager;
+    private final View mLeftIcon;
+    private final View mCenterIcon;
+    private final View mRightIcon;
+    private Interpolator mFastOutSlowIn;
+    private Animator mSwipeAnimator;
+    private boolean mCallbackCalled;
+
+    KeyguardPageSwipeHelper(Callback callback, Context context) {
+        mContext = context;
+        mCallback = callback;
+        mLeftIcon = mCallback.getLeftIcon();
+        mCenterIcon = mCallback.getCenterIcon();
+        mRightIcon = mCallback.getRightIcon();
+        updateIcon(mLeftIcon, 1.0f, SWIPE_RESTING_ALPHA_AMOUNT, false);
+        updateIcon(mCenterIcon, 1.0f, SWIPE_RESTING_ALPHA_AMOUNT, false);
+        updateIcon(mRightIcon, 1.0f, SWIPE_RESTING_ALPHA_AMOUNT, false);
+        mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
+        initDimens();
+    }
+
+    private void initDimens() {
+        final ViewConfiguration configuration = ViewConfiguration.get(mContext);
+        mTouchSlop = configuration.getScaledTouchSlop();
+        mMinFlingVelocity = configuration.getScaledMinimumFlingVelocity();
+        mMinTranslationAmount = mContext.getResources().getDimensionPixelSize(
+                R.dimen.keyguard_min_swipe_amount);
+        mFlingAnimationUtils = new FlingAnimationUtils(mContext, 0.4f);
+        mFastOutSlowIn = AnimationUtils.loadInterpolator(mContext,
+                android.R.interpolator.fast_out_slow_in);
+    }
+
+    public boolean onTouchEvent(MotionEvent event) {
+        int pointerIndex = event.findPointerIndex(mTrackingPointer);
+        if (pointerIndex < 0) {
+            pointerIndex = 0;
+            mTrackingPointer = event.getPointerId(pointerIndex);
+        }
+        final float y = event.getY(pointerIndex);
+        final float x = event.getX(pointerIndex);
+
+        switch (event.getActionMasked()) {
+            case MotionEvent.ACTION_DOWN:
+                if (mSwipingInProgress) {
+                    cancelAnimations();
+                }
+                mInitialTouchY = y;
+                mInitialTouchX = x;
+                mTranslationOnDown = mTranslation;
+                initVelocityTracker();
+                trackMovement(event);
+                break;
+
+            case MotionEvent.ACTION_POINTER_UP:
+                final int upPointer = event.getPointerId(event.getActionIndex());
+                if (mTrackingPointer == upPointer) {
+                    // gesture is ongoing, find a new pointer to track
+                    final int newIndex = event.getPointerId(0) != upPointer ? 0 : 1;
+                    final float newY = event.getY(newIndex);
+                    final float newX = event.getX(newIndex);
+                    mTrackingPointer = event.getPointerId(newIndex);
+                    mInitialTouchY = newY;
+                    mInitialTouchX = newX;
+                    mTranslationOnDown = mTranslation;
+                }
+                break;
+
+            case MotionEvent.ACTION_MOVE:
+                final float w = x - mInitialTouchX;
+                trackMovement(event);
+                if (((leftSwipePossible() && w > mTouchSlop)
+                        || (rightSwipePossible() && w < -mTouchSlop))
+                        && Math.abs(w) > Math.abs(y - mInitialTouchY)
+                        && !mSwipingInProgress) {
+                    cancelAnimations();
+                    mInitialTouchY = y;
+                    mInitialTouchX = x;
+                    mTranslationOnDown = mTranslation;
+                    mSwipingInProgress = true;
+                }
+                if (mSwipingInProgress) {
+                    setTranslation(mTranslationOnDown + x - mInitialTouchX, false);
+                    onUserActivity(event.getEventTime());
+                }
+                break;
+
+            case MotionEvent.ACTION_UP:
+            case MotionEvent.ACTION_CANCEL:
+                mTrackingPointer = -1;
+                trackMovement(event);
+                if (mSwipingInProgress) {
+                    flingWithCurrentVelocity();
+                }
+                if (mVelocityTracker != null) {
+                    mVelocityTracker.recycle();
+                    mVelocityTracker = null;
+                }
+                break;
+        }
+        return true;
+    }
+
+    private boolean rightSwipePossible() {
+        return mRightIcon.getVisibility() == View.VISIBLE;
+    }
+
+    private boolean leftSwipePossible() {
+        return mLeftIcon.getVisibility() == View.VISIBLE;
+    }
+
+    public boolean onInterceptTouchEvent(MotionEvent ev) {
+        return false;
+    }
+
+    private void onUserActivity(long when) {
+        mPowerManager.userActivity(when, false);
+    }
+
+    private void cancelAnimations() {
+        ArrayList<View> targetViews = mCallback.getTranslationViews();
+        for (View target : targetViews) {
+            target.animate().cancel();
+        }
+        View targetView = mTranslation > 0 ? mLeftIcon : mRightIcon;
+        targetView.animate().cancel();
+        if (mSwipeAnimator != null) {
+            mSwipeAnimator.removeAllListeners();
+            mSwipeAnimator.cancel();
+            hideInactiveIcons(true);
+        }
+    }
+
+    private void flingWithCurrentVelocity() {
+        float vel = getCurrentVelocity();
+
+        // We snap back if the current translation is not far enough
+        boolean snapBack = Math.abs(mTranslation) < mMinTranslationAmount;
+
+        // or if the velocity is in the opposite direction.
+        boolean velIsInWrongDirection = vel * mTranslation < 0;
+        snapBack |= Math.abs(vel) > mMinFlingVelocity && velIsInWrongDirection;
+        vel = snapBack ^ velIsInWrongDirection ? 0 : vel;
+        fling(vel, snapBack);
+    }
+
+    private void fling(float vel, final boolean snapBack) {
+        float target = mTranslation < 0 ? -mCallback.getPageWidth() : mCallback.getPageWidth();
+        target = snapBack ? 0 : target;
+
+        // translation Animation
+        startTranslationAnimations(vel, target);
+
+        // animate left / right icon
+        startIconAnimation(vel, snapBack, target);
+
+        ValueAnimator animator = ValueAnimator.ofFloat(mTranslation, target);
+        mFlingAnimationUtils.apply(animator, mTranslation, target, vel);
+        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+            @Override
+            public void onAnimationUpdate(ValueAnimator animation) {
+                mTranslation = (float) animation.getAnimatedValue();
+            }
+        });
+        animator.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                mSwipeAnimator = null;
+                mSwipingInProgress = false;
+                if (!snapBack && !mCallbackCalled) {
+
+                    // ensure that the callback is called eventually
+                    mCallback.onAnimationToSideStarted(mTranslation < 0);
+                    mCallbackCalled = true;
+                }
+            }
+        });
+        if (!snapBack) {
+            mCallbackCalled = false;
+            animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+                int frameNumber;
+
+                @Override
+                public void onAnimationUpdate(ValueAnimator animation) {
+                    if (frameNumber == 2 && !mCallbackCalled) {
+
+                        // we have to wait for the second frame for this call,
+                        // until the render thread has definitely kicked in, to avoid a lag.
+                        mCallback.onAnimationToSideStarted(mTranslation < 0);
+                        mCallbackCalled = true;
+                    }
+                    frameNumber++;
+                }
+            });
+        } else {
+            showAllIcons(true);
+        }
+        animator.start();
+        mSwipeAnimator = animator;
+    }
+
+    private void startTranslationAnimations(float vel, float target) {
+        ArrayList<View> targetViews = mCallback.getTranslationViews();
+        for (View targetView : targetViews) {
+            ViewPropertyAnimator animator = targetView.animate();
+            mFlingAnimationUtils.apply(animator, mTranslation, target, vel);
+            animator.translationX(target);
+        }
+    }
+
+    private void startIconAnimation(float vel, boolean snapBack, float target) {
+        float scale = snapBack ? 1.0f : SWIPE_MAX_ICON_SCALE_AMOUNT;
+        float alpha = snapBack ? SWIPE_RESTING_ALPHA_AMOUNT : 1.0f;
+        View targetView = mTranslation > 0
+                ? mLeftIcon
+                : mRightIcon;
+        if (targetView.getVisibility() == View.VISIBLE) {
+            ViewPropertyAnimator iconAnimator = targetView.animate();
+            mFlingAnimationUtils.apply(iconAnimator, mTranslation, target, vel);
+            iconAnimator.scaleX(scale);
+            iconAnimator.scaleY(scale);
+            iconAnimator.alpha(alpha);
+        }
+    }
+
+    private void setTranslation(float translation, boolean isReset) {
+        translation = rightSwipePossible() ? translation : Math.max(0, translation);
+        translation = leftSwipePossible() ? translation : Math.min(0, translation);
+        if (translation != mTranslation) {
+            ArrayList<View> translatedViews = mCallback.getTranslationViews();
+            for (View view : translatedViews) {
+                view.setTranslationX(translation);
+            }
+            if (translation == 0.0f) {
+                boolean animate = !isReset;
+                showAllIcons(animate);
+            } else {
+                View targetView = translation > 0 ? mLeftIcon : mRightIcon;
+                float progress = Math.abs(translation) / mCallback.getPageWidth();
+                progress = Math.min(progress, 1.0f);
+                float alpha = SWIPE_RESTING_ALPHA_AMOUNT * (1.0f - progress) + progress;
+                float scale = (1.0f - progress) + progress * SWIPE_MAX_ICON_SCALE_AMOUNT;
+                updateIcon(targetView, scale, alpha, false);
+                View otherView = translation < 0 ? mLeftIcon : mRightIcon;
+                if (mTranslation * translation <= 0) {
+                    // The sign of the translation has changed so we need to hide the other icons
+                    updateIcon(otherView, 0, 0, true);
+                    updateIcon(mCenterIcon, 0, 0, true);
+                }
+            }
+            mTranslation = translation;
+        }
+    }
+
+    private void showAllIcons(boolean animate) {
+        float scale = 1.0f;
+        float alpha = SWIPE_RESTING_ALPHA_AMOUNT;
+        updateIcon(mRightIcon, scale, alpha, animate);
+        updateIcon(mCenterIcon, scale, alpha, animate);
+        updateIcon(mLeftIcon, scale, alpha, animate);
+    }
+
+    private void hideInactiveIcons(boolean animate){
+        View otherView = mTranslation < 0 ? mLeftIcon : mRightIcon;
+        updateIcon(otherView, 0, 0, animate);
+        updateIcon(mCenterIcon, 0, 0, animate);
+    }
+
+    private void updateIcon(View view, float scale, float alpha, boolean animate) {
+        if (view.getVisibility() != View.VISIBLE) {
+            return;
+        }
+        if (!animate) {
+            view.setAlpha(alpha);
+            view.setScaleX(scale);
+            view.setScaleY(scale);
+            // TODO: remove this invalidate once the property setters invalidate it properly
+            view.invalidate();
+        } else {
+            if (view.getAlpha() != alpha || view.getScaleX() != scale) {
+                view.animate()
+                        .setInterpolator(mFastOutSlowIn)
+                        .alpha(alpha)
+                        .scaleX(scale)
+                        .scaleY(scale);
+            }
+        }
+    }
+
+    private void trackMovement(MotionEvent event) {
+        if (mVelocityTracker != null) {
+            mVelocityTracker.addMovement(event);
+        }
+    }
+
+    private void initVelocityTracker() {
+        if (mVelocityTracker != null) {
+            mVelocityTracker.recycle();
+        }
+        mVelocityTracker = VelocityTracker.obtain();
+    }
+
+    private float getCurrentVelocity() {
+        if (mVelocityTracker == null) {
+            return 0;
+        }
+        mVelocityTracker.computeCurrentVelocity(1000);
+        return mVelocityTracker.getXVelocity();
+    }
+
+    public void onConfigurationChanged() {
+        initDimens();
+    }
+
+    public void reset() {
+        setTranslation(0.0f, true);
+        mSwipingInProgress = false;
+    }
+
+    public boolean isSwipingInProgress() {
+        return mSwipingInProgress;
+    }
+
+    public interface Callback {
+
+        /**
+         * Notifies the callback when an animation to a side page was started.
+         *
+         * @param rightPage Is the page animated to the right page?
+         */
+        void onAnimationToSideStarted(boolean rightPage);
+
+        float getPageWidth();
+
+        ArrayList<View> getTranslationViews();
+
+        View getLeftIcon();
+
+        View getCenterIcon();
+
+        View getRightIcon();
+    }
+}
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 52688df..838e5e3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -21,6 +21,7 @@
 import android.animation.ObjectAnimator;
 import android.animation.ValueAnimator;
 import android.content.Context;
+import android.content.res.Configuration;
 import android.util.AttributeSet;
 import android.view.MotionEvent;
 import android.view.VelocityTracker;
@@ -40,10 +41,13 @@
 import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
 import com.android.systemui.statusbar.stack.StackStateAnimator;
 
+import java.util.ArrayList;
+
 public class NotificationPanelView extends PanelView implements
         ExpandableView.OnHeightChangedListener, ObservableScrollView.Listener,
-        View.OnClickListener {
+        View.OnClickListener, KeyguardPageSwipeHelper.Callback {
 
+    private KeyguardPageSwipeHelper mPageSwiper;
     PhoneStatusBar mStatusBar;
     private StatusBarHeaderView mHeader;
     private View mQsContainer;
@@ -58,7 +62,7 @@
 
     private int mTrackingPointer;
     private VelocityTracker mVelocityTracker;
-    private boolean mTracking;
+    private boolean mQsTracking;
 
     /**
      * Whether we are currently handling a motion gesture in #onInterceptTouchEvent, but haven't
@@ -92,6 +96,11 @@
             new KeyguardClockPositionAlgorithm();
     private KeyguardClockPositionAlgorithm.Result mClockPositionResult =
             new KeyguardClockPositionAlgorithm.Result();
+    private boolean mIsSwipedHorizontally;
+    private boolean mIsExpanding;
+    private KeyguardBottomAreaView mKeyguardBottomArea;
+    private boolean mBlockTouches;
+    private ArrayList<View> mSwipeTranslationViews = new ArrayList<>();
 
     public NotificationPanelView(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -129,6 +138,10 @@
         mNotificationStackScroller.setOnHeightChangedListener(this);
         mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(getContext(),
                 android.R.interpolator.fast_out_slow_in);
+        mKeyguardBottomArea = (KeyguardBottomAreaView) findViewById(R.id.keyguard_bottom_area);
+        mSwipeTranslationViews.add(mNotificationStackScroller);
+        mSwipeTranslationViews.add(mKeyguardStatusView);
+        mPageSwiper = new KeyguardPageSwipeHelper(this, getContext());
     }
 
     @Override
@@ -247,6 +260,12 @@
         mQsExpansionEnabled = qsExpansionEnabled;
     }
 
+    public void resetViews() {
+        mBlockTouches = false;
+        mPageSwiper.reset();
+        closeQs();
+    }
+
     public void closeQs() {
         cancelAnimation();
         setQsExpansion(mQsMinExpansionHeight);
@@ -263,9 +282,7 @@
     public void fling(float vel, boolean always) {
         GestureRecorder gr = ((PhoneStatusBarView) mBar).mBar.getGestureRecorder();
         if (gr != null) {
-            gr.tag(
-                "fling " + ((vel > 0) ? "open" : "closed"),
-                "notifications,v=" + vel);
+            gr.tag("fling " + ((vel > 0) ? "open" : "closed"), "notifications,v=" + vel);
         }
         super.fling(vel, always);
     }
@@ -283,6 +300,9 @@
 
     @Override
     public boolean onInterceptTouchEvent(MotionEvent event) {
+        if (mBlockTouches) {
+            return false;
+        }
         int pointerIndex = event.findPointerIndex(mTrackingPointer);
         if (pointerIndex < 0) {
             pointerIndex = 0;
@@ -298,7 +318,7 @@
                 mInitialTouchX = x;
                 initVelocityTracker();
                 trackMovement(event);
-                if (shouldIntercept(mInitialTouchX, mInitialTouchY, 0)) {
+                if (shouldQuickSettingsIntercept(mInitialTouchX, mInitialTouchY, 0)) {
                     getParent().requestDisallowInterceptTouchEvent(true);
                 }
                 break;
@@ -316,7 +336,7 @@
             case MotionEvent.ACTION_MOVE:
                 final float h = y - mInitialTouchY;
                 trackMovement(event);
-                if (mTracking) {
+                if (mQsTracking) {
 
                     // Already tracking because onOverscrolled was called. We need to update here
                     // so we don't stop for a frame until the next touch event gets handled in
@@ -327,12 +347,12 @@
                     return true;
                 }
                 if (Math.abs(h) > mTouchSlop && Math.abs(h) > Math.abs(x - mInitialTouchX)
-                        && shouldIntercept(mInitialTouchX, mInitialTouchY, h)) {
+                        && shouldQuickSettingsIntercept(mInitialTouchX, mInitialTouchY, h)) {
                     onQsExpansionStarted();
                     mInitialHeightOnTouch = mQsExpansionHeight;
                     mInitialTouchY = y;
                     mInitialTouchX = x;
-                    mTracking = true;
+                    mQsTracking = true;
                     mIntercepting = false;
                     return true;
                 }
@@ -341,9 +361,9 @@
             case MotionEvent.ACTION_CANCEL:
             case MotionEvent.ACTION_UP:
                 trackMovement(event);
-                if (mTracking) {
-                    flingWithCurrentVelocity();
-                    mTracking = false;
+                if (mQsTracking) {
+                    flingQsWithCurrentVelocity();
+                    mQsTracking = false;
                 }
                 mIntercepting = false;
                 break;
@@ -362,7 +382,7 @@
         super.requestDisallowInterceptTouchEvent(disallowIntercept);
     }
 
-    private void flingWithCurrentVelocity() {
+    private void flingQsWithCurrentVelocity() {
         float vel = getCurrentVelocity();
 
         // TODO: Better logic whether we should expand or not.
@@ -371,65 +391,83 @@
 
     @Override
     public boolean onTouchEvent(MotionEvent event) {
+        if (mBlockTouches) {
+            return false;
+        }
         // TODO: Handle doublefinger swipe to notifications again. Look at history for a reference
         // implementation.
-        if (mTracking) {
-            int pointerIndex = event.findPointerIndex(mTrackingPointer);
-            if (pointerIndex < 0) {
-                pointerIndex = 0;
-                mTrackingPointer = event.getPointerId(pointerIndex);
+        if (!mIsExpanding && !mQsExpanded && mStatusBar.getBarState() != StatusBarState.SHADE) {
+            mPageSwiper.onTouchEvent(event);
+            if (mPageSwiper.isSwipingInProgress()) {
+                return true;
             }
-            final float y = event.getY(pointerIndex);
-            final float x = event.getX(pointerIndex);
-
-            switch (event.getActionMasked()) {
-                case MotionEvent.ACTION_DOWN:
-                    mTracking = true;
-                    mInitialTouchY = y;
-                    mInitialTouchX = x;
-                    onQsExpansionStarted();
-                    mInitialHeightOnTouch = mQsExpansionHeight;
-                    initVelocityTracker();
-                    trackMovement(event);
-                    break;
-
-                case MotionEvent.ACTION_POINTER_UP:
-                    final int upPointer = event.getPointerId(event.getActionIndex());
-                    if (mTrackingPointer == upPointer) {
-                        // gesture is ongoing, find a new pointer to track
-                        final int newIndex = event.getPointerId(0) != upPointer ? 0 : 1;
-                        final float newY = event.getY(newIndex);
-                        final float newX = event.getX(newIndex);
-                        mTrackingPointer = event.getPointerId(newIndex);
-                        mInitialHeightOnTouch = mQsExpansionHeight;
-                        mInitialTouchY = newY;
-                        mInitialTouchX = newX;
-                    }
-                    break;
-
-                case MotionEvent.ACTION_MOVE:
-                    final float h = y - mInitialTouchY;
-                    setQsExpansion(h + mInitialHeightOnTouch);
-                    trackMovement(event);
-                    break;
-
-                case MotionEvent.ACTION_UP:
-                case MotionEvent.ACTION_CANCEL:
-                    mTracking = false;
-                    mTrackingPointer = -1;
-                    trackMovement(event);
-                    flingWithCurrentVelocity();
-                    if (mVelocityTracker != null) {
-                        mVelocityTracker.recycle();
-                        mVelocityTracker = null;
-                    }
-                    break;
-            }
-            return true;
+        }
+        if (mQsTracking || mQsExpanded) {
+            return onQsTouch(event);
         }
 
-        // Consume touch events when QS are expanded.
-        return mQsExpanded || super.onTouchEvent(event);
+        super.onTouchEvent(event);
+        return true;
+    }
+
+    @Override
+    protected boolean hasConflictingGestures() {
+        return mStatusBar.getBarState() != StatusBarState.SHADE;
+    }
+
+    private boolean onQsTouch(MotionEvent event) {
+        int pointerIndex = event.findPointerIndex(mTrackingPointer);
+        if (pointerIndex < 0) {
+            pointerIndex = 0;
+            mTrackingPointer = event.getPointerId(pointerIndex);
+        }
+        final float y = event.getY(pointerIndex);
+        final float x = event.getX(pointerIndex);
+
+        switch (event.getActionMasked()) {
+            case MotionEvent.ACTION_DOWN:
+                mQsTracking = true;
+                mInitialTouchY = y;
+                mInitialTouchX = x;
+                onQsExpansionStarted();
+                mInitialHeightOnTouch = mQsExpansionHeight;
+                initVelocityTracker();
+                trackMovement(event);
+                break;
+
+            case MotionEvent.ACTION_POINTER_UP:
+                final int upPointer = event.getPointerId(event.getActionIndex());
+                if (mTrackingPointer == upPointer) {
+                    // gesture is ongoing, find a new pointer to track
+                    final int newIndex = event.getPointerId(0) != upPointer ? 0 : 1;
+                    final float newY = event.getY(newIndex);
+                    final float newX = event.getX(newIndex);
+                    mTrackingPointer = event.getPointerId(newIndex);
+                    mInitialHeightOnTouch = mQsExpansionHeight;
+                    mInitialTouchY = newY;
+                    mInitialTouchX = newX;
+                }
+                break;
+
+            case MotionEvent.ACTION_MOVE:
+                final float h = y - mInitialTouchY;
+                setQsExpansion(h + mInitialHeightOnTouch);
+                trackMovement(event);
+                break;
+
+            case MotionEvent.ACTION_UP:
+            case MotionEvent.ACTION_CANCEL:
+                mQsTracking = false;
+                mTrackingPointer = -1;
+                trackMovement(event);
+                flingQsWithCurrentVelocity();
+                if (mVelocityTracker != null) {
+                    mVelocityTracker.recycle();
+                    mVelocityTracker = null;
+                }
+                break;
+        }
+        return true;
     }
 
     @Override
@@ -439,7 +477,7 @@
             mInitialHeightOnTouch = mQsExpansionHeight;
             mInitialTouchY = mLastTouchY;
             mInitialTouchX = mLastTouchX;
-            mTracking = true;
+            mQsTracking = true;
         }
     }
 
@@ -569,7 +607,7 @@
     /**
      * @return Whether we should intercept a gesture to open Quick Settings.
      */
-    private boolean shouldIntercept(float x, float y, float yDiff) {
+    private boolean shouldQuickSettingsIntercept(float x, float y, float yDiff) {
         if (!mQsExpansionEnabled) {
             return false;
         }
@@ -647,12 +685,14 @@
     protected void onExpandingStarted() {
         super.onExpandingStarted();
         mNotificationStackScroller.onExpansionStarted();
+        mIsExpanding = true;
     }
 
     @Override
     protected void onExpandingFinished() {
         super.onExpandingFinished();
         mNotificationStackScroller.onExpansionStopped();
+        mIsExpanding = false;
     }
 
     @Override
@@ -686,6 +726,12 @@
     }
 
     @Override
+    protected void onConfigurationChanged(Configuration newConfig) {
+        super.onConfigurationChanged(newConfig);
+        mPageSwiper.onConfigurationChanged();
+    }
+
+    @Override
     public void onClick(View v) {
         if (v == mHeader.getBackgroundView()) {
             onQsExpansionStarted();
@@ -696,4 +742,40 @@
             }
         }
     }
+
+    @Override
+    public void onAnimationToSideStarted(boolean rightPage) {
+        if (rightPage) {
+            mKeyguardBottomArea.launchCamera();
+        } else {
+            mKeyguardBottomArea.launchPhone();
+        }
+        mBlockTouches = true;
+    }
+
+
+    @Override
+    public float getPageWidth() {
+        return getWidth();
+    }
+
+    @Override
+    public ArrayList<View> getTranslationViews() {
+        return mSwipeTranslationViews;
+    }
+
+    @Override
+    public View getLeftIcon() {
+        return mKeyguardBottomArea.getPhoneImageView();
+    }
+
+    @Override
+    public View getCenterIcon() {
+        return mKeyguardBottomArea.getLockIcon();
+    }
+
+    @Override
+    public View getRightIcon() {
+        return mKeyguardBottomArea.getCameraImageView();
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
index 7500c10..f6db8a8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
@@ -35,7 +35,7 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 
-public class PanelView extends FrameLayout {
+public abstract class PanelView extends FrameLayout {
     public static final boolean DEBUG = PanelBar.DEBUG;
     public static final String TAG = PanelView.class.getSimpleName();
     protected float mOverExpansion;
@@ -130,21 +130,24 @@
         final float y = event.getY(pointerIndex);
         final float x = event.getX(pointerIndex);
 
+        boolean waitForTouchSlop = hasConflictingGestures();
+
         switch (event.getActionMasked()) {
             case MotionEvent.ACTION_DOWN:
-                mTracking = true;
 
                 mInitialTouchY = y;
                 mInitialTouchX = x;
+                mInitialOffsetOnTouch = mExpandedHeight;
                 if (mVelocityTracker == null) {
                     initVelocityTracker();
                 }
                 trackMovement(event);
-                if (mHeightAnimator != null) {
-                    mHeightAnimator.cancel(); // end any outstanding animations
+                if (!waitForTouchSlop || mHeightAnimator != null) {
+                    if (mHeightAnimator != null) {
+                        mHeightAnimator.cancel(); // end any outstanding animations
+                    }
+                    onTrackingStarted();
                 }
-                onTrackingStarted();
-                mInitialOffsetOnTouch = mExpandedHeight;
                 if (mExpandedHeight == 0) {
                     mJustPeeked = true;
                     runPeekAnimation();
@@ -166,15 +169,27 @@
                 break;
 
             case MotionEvent.ACTION_MOVE:
-                final float h = y - mInitialTouchY + mInitialOffsetOnTouch;
-                if (h > mPeekHeight) {
+                float h = y - mInitialTouchY;
+                if (waitForTouchSlop && !mTracking && Math.abs(h) > mTouchSlop
+                        && Math.abs(h) > Math.abs(x - mInitialTouchX)) {
+                    mInitialOffsetOnTouch = mExpandedHeight;
+                    mInitialTouchX = x;
+                    mInitialTouchY = y;
+                    if (mHeightAnimator != null) {
+                        mHeightAnimator.cancel(); // end any outstanding animations
+                    }
+                    onTrackingStarted();
+                    h = 0;
+                }
+                final float newHeight = h + mInitialOffsetOnTouch;
+                if (newHeight > mPeekHeight) {
                     if (mPeekAnimator != null && mPeekAnimator.isStarted()) {
                         mPeekAnimator.cancel();
                     }
                     mJustPeeked = false;
                 }
-                if (!mJustPeeked) {
-                    setExpandedHeightInternal(h);
+                if (!mJustPeeked && (!waitForTouchSlop || mTracking)) {
+                    setExpandedHeightInternal(newHeight);
                     mBar.panelExpansionChanged(PanelView.this, mExpandedFraction);
                 }
 
@@ -183,7 +198,6 @@
 
             case MotionEvent.ACTION_UP:
             case MotionEvent.ACTION_CANCEL:
-                mTracking = false;
                 mTrackingPointer = -1;
                 trackMovement(event);
                 boolean expand = flingWithCurrentVelocity();
@@ -194,14 +208,18 @@
                 }
                 break;
         }
-        return true;
+        return !waitForTouchSlop || mTracking;
     }
 
+    protected abstract boolean hasConflictingGestures();
+
     protected void onTrackingStopped(boolean expand) {
+        mTracking = false;
         mBar.onTrackingStopped(PanelView.this, expand);
     }
 
     protected void onTrackingStarted() {
+        mTracking = true;
         mBar.onTrackingStarted(PanelView.this);
         onExpandingStarted();
     }
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 15ad709..5dcd61c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -64,6 +64,7 @@
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.provider.Settings.Global;
+import android.service.notification.NotificationListenerService.Ranking;
 import android.service.notification.StatusBarNotification;
 import android.util.ArraySet;
 import android.util.DisplayMetrics;
@@ -1037,13 +1038,15 @@
     }
 
     @Override
-    public void addNotificationInternal(StatusBarNotification notification) {
+    public void addNotificationInternal(StatusBarNotification notification, Ranking ranking) {
         if (DEBUG) Log.d(TAG, "addNotification score=" + notification.getScore());
         Entry shadeEntry = createNotificationViews(notification);
         if (shadeEntry == null) {
             return;
         }
         if (mZenMode != Global.ZEN_MODE_OFF && mIntercepted.tryIntercept(notification)) {
+            // Forward the ranking so we can sort the new notification.
+            mNotificationData.updateRanking(ranking);
             return;
         }
         if (mUseHeadsUp && shouldInterrupt(notification)) {
@@ -1083,7 +1086,7 @@
                 tick(notification, true);
             }
         }
-        addNotificationViews(shadeEntry);
+        addNotificationViews(shadeEntry, ranking);
         // Recalculate the position of the sliding windows and the titles.
         setAreThereNotifications();
         updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
@@ -1099,14 +1102,14 @@
     }
 
     @Override
-    public void updateNotification(StatusBarNotification notification) {
-        super.updateNotification(notification);
+    public void updateNotificationInternal(StatusBarNotification notification, Ranking ranking) {
+        super.updateNotificationInternal(notification, ranking);
         mIntercepted.update(notification);
     }
 
     @Override
-    public void removeNotificationInternal(String key) {
-        StatusBarNotification old = removeNotificationViews(key);
+    public void removeNotificationInternal(String key, Ranking ranking) {
+        StatusBarNotification old = removeNotificationViews(key, ranking);
         if (SPEW) Log.d(TAG, "removeNotification key=" + key + " old=" + old);
 
         if (old != null) {
@@ -1143,7 +1146,7 @@
             R.integer.config_show_search_delay);
     }
 
-    private void loadNotificationShade() {
+    private void updateNotificationShade() {
         if (mStackScroller == null) return;
 
         int N = mNotificationData.size();
@@ -1153,7 +1156,7 @@
         final boolean provisioned = isDeviceProvisioned();
         // If the device hasn't been through Setup, we only show system notifications
         for (int i=0; i<N; i++) {
-            Entry ent = mNotificationData.get(N-i-1);
+            Entry ent = mNotificationData.get(i);
             if (!(provisioned || showNotificationEvenIfUnprovisioned(ent.notification))) continue;
 
             // TODO How do we want to badge notifcations from profiles.
@@ -1184,26 +1187,75 @@
         for (int i=0; i<toShow.size(); i++) {
             View v = toShow.get(i);
             if (v.getParent() == null) {
-                mStackScroller.addView(v, i);
+                mStackScroller.addView(v);
             }
         }
 
+        // So after all this work notifications still aren't sorted correctly.
+        // Let's do that now by advancing through toShow and mStackScroller in
+        // lock-step, making sure mStackScroller matches what we see in toShow.
+        int j = 0;
+        for (int i = 0; i < mStackScroller.getChildCount(); i++) {
+            View child = mStackScroller.getChildAt(i);
+            if (!(child instanceof ExpandableNotificationRow)) {
+                // We don't care about non-notification views.
+                continue;
+            }
+
+            if (child == toShow.get(j)) {
+                // Everything is well, advance both lists.
+                j++;
+                continue;
+            }
+
+            // Oops, wrong notification at this position. Put the right one
+            // here and advance both lists.
+            mStackScroller.changeViewPosition(toShow.get(j), i);
+            j++;
+        }
+        updateRowStates();
+        updateSpeedbump();
         mNotificationPanel.setQsExpansionEnabled(provisioned && mUserSetup);
     }
 
+    private void updateSpeedbump() {
+        int speedbumpIndex = -1;
+        int currentIndex = 0;
+        for (int i = 0; i < mNotificationData.size(); i++) {
+            Entry entry = mNotificationData.get(i);
+            if (entry.row.getParent() == null) {
+                // This view isn't even added, so the stack scroller doesn't
+                // know about it. Ignore completely.
+                continue;
+            }
+            if (entry.row.getVisibility() != View.GONE &&
+                    mNotificationData.isAmbient(entry.key)) {
+                speedbumpIndex = currentIndex;
+                break;
+            }
+            currentIndex++;
+        }
+        mStackScroller.updateSpeedBumpIndex(speedbumpIndex);
+    }
+
     @Override
-    protected void updateNotificationIcons() {
+    protected void updateNotifications() {
+        // TODO: Move this into updateNotificationIcons()?
         if (mNotificationIcons == null) return;
 
-        loadNotificationShade();
+        updateNotificationShade();
+        updateNotificationIcons();
+    }
 
+    private void updateNotificationIcons() {
         final LinearLayout.LayoutParams params
             = new LinearLayout.LayoutParams(mIconSize + 2*mIconHPadding, mNaturalBarHeight);
 
         int N = mNotificationData.size();
 
         if (DEBUG) {
-            Log.d(TAG, "refreshing icons: " + N + " notifications, mNotificationIcons=" + mNotificationIcons);
+            Log.d(TAG, "refreshing icons: " + N + " notifications, mNotificationIcons=" +
+                    mNotificationIcons);
         }
 
         ArrayList<View> toShow = new ArrayList<View>();
@@ -1211,7 +1263,7 @@
         final boolean provisioned = isDeviceProvisioned();
         // If the device hasn't been through Setup, we only show system notifications
         for (int i=0; i<N; i++) {
-            Entry ent = mNotificationData.get(N-i-1);
+            Entry ent = mNotificationData.get(i);
             if (!((provisioned && ent.notification.getScore() >= HIDE_ICONS_BELOW_SCORE)
                     || showNotificationEvenIfUnprovisioned(ent.notification))) continue;
             if (!notificationIsForCurrentProfiles(ent.notification)) continue;
@@ -2388,7 +2440,7 @@
     public void userSwitched(int newUserId) {
         if (MULTIUSER_DEBUG) mNotificationPanelDebugText.setText("USER " + newUserId);
         animateCollapsePanels();
-        updateNotificationIcons();
+        updateNotifications();
         resetUserSetupObserver();
     }
 
@@ -2774,7 +2826,7 @@
             mKeyguardStatusView.setVisibility(View.VISIBLE);
             mKeyguardIndicationTextView.setVisibility(View.VISIBLE);
             mKeyguardIndicationTextView.switchIndication(mKeyguardHotwordPhrase);
-            mNotificationPanel.closeQs();
+            mNotificationPanel.resetViews();
         } else {
             mKeyguardStatusView.setVisibility(View.GONE);
             mKeyguardIndicationTextView.setVisibility(View.GONE);
@@ -2793,10 +2845,8 @@
 
         updateStackScrollerState();
         updatePublicMode();
-        updateRowStates();
-        updateSpeedBump();
+        updateNotifications();
         checkBarModes();
-        updateNotificationIcons();
         updateCarrierLabelVisibility(false);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SwipeAffordanceView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SwipeAffordanceView.java
deleted file mode 100644
index 049c5fc..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SwipeAffordanceView.java
+++ /dev/null
@@ -1,222 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.systemui.statusbar.phone;
-
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.os.SystemClock;
-import android.util.AttributeSet;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewConfiguration;
-import android.view.animation.AccelerateInterpolator;
-import android.view.animation.DecelerateInterpolator;
-import android.widget.Button;
-
-import com.android.systemui.R;
-import com.android.systemui.statusbar.policy.KeyButtonView;
-
-/**
- * A swipeable button for affordances on the lockscreen. This is used for the camera and phone
- * affordance.
- */
-public class SwipeAffordanceView extends KeyButtonView {
-
-    private static final int SWIPE_DIRECTION_START = 0;
-    private static final int SWIPE_DIRECTION_END = 1;
-
-    private static final int SWIPE_DIRECTION_LEFT = 0;
-    private static final int SWIPE_DIRECTION_RIGHT = 1;
-
-    private AffordanceListener mListener;
-    private int mScaledTouchSlop;
-    private float mDragDistance;
-    private int mResolvedSwipeDirection;
-    private int mSwipeDirection;
-
-    public SwipeAffordanceView(Context context, AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    public SwipeAffordanceView(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
-        TypedArray a = context.getTheme().obtainStyledAttributes(
-                attrs,
-                R.styleable.SwipeAffordanceView,
-                0, 0);
-        try {
-            mSwipeDirection = a.getInt(R.styleable.SwipeAffordanceView_swipeDirection, 0);
-        } finally {
-            a.recycle();
-        }
-    }
-
-    @Override
-    public void onRtlPropertiesChanged(int layoutDirection) {
-        super.onRtlPropertiesChanged(layoutDirection);
-        if (!isLayoutRtl()) {
-            mResolvedSwipeDirection = mSwipeDirection;
-        } else {
-            mResolvedSwipeDirection = mSwipeDirection == SWIPE_DIRECTION_START
-                    ? SWIPE_DIRECTION_RIGHT
-                    : SWIPE_DIRECTION_LEFT;
-        }
-    }
-
-    @Override
-    protected void onFinishInflate() {
-        super.onFinishInflate();
-        mDragDistance = getResources().getDimension(R.dimen.affordance_drag_distance);
-        mScaledTouchSlop = ViewConfiguration.get(mContext).getScaledTouchSlop();
-    }
-
-    public void enableAccessibility(boolean touchExplorationEnabled) {
-
-        // Add a touch handler or accessibility click listener for camera button.
-        if (touchExplorationEnabled) {
-            setOnTouchListener(null);
-            setOnClickListener(mClickListener);
-        } else {
-            setOnTouchListener(mTouchListener);
-            setOnClickListener(null);
-        }
-    }
-
-    public void setAffordanceListener(AffordanceListener listener) {
-        mListener = listener;
-    }
-
-    private void onActionPerformed() {
-        if (mListener != null) {
-            mListener.onActionPerformed(this);
-        }
-    }
-
-    private void onUserActivity(long when) {
-        if (mListener != null) {
-            mListener.onUserActivity(when);
-        }
-    }
-
-    private final OnClickListener mClickListener = new OnClickListener() {
-        @Override
-        public void onClick(View v) {
-            onActionPerformed();
-        }
-    };
-
-    private final OnTouchListener mTouchListener = new OnTouchListener() {
-        private float mStartX;
-        private boolean mTouchSlopReached;
-        private boolean mSkipCancelAnimation;
-
-        @Override
-        public boolean onTouch(final View view, MotionEvent event) {
-            float realX = event.getRawX();
-            switch (event.getAction()) {
-                case MotionEvent.ACTION_DOWN:
-                    mStartX = realX;
-                    mTouchSlopReached = false;
-                    mSkipCancelAnimation = false;
-                    break;
-                case MotionEvent.ACTION_MOVE:
-                    if (mResolvedSwipeDirection == SWIPE_DIRECTION_LEFT
-                            ? realX > mStartX
-                            : realX < mStartX) {
-                        realX = mStartX;
-                    }
-                    if (mResolvedSwipeDirection == SWIPE_DIRECTION_LEFT
-                            ? realX < mStartX - mDragDistance
-                            : realX > mStartX + mDragDistance) {
-                        view.setPressed(true);
-                        onUserActivity(event.getEventTime());
-                    } else {
-                        view.setPressed(false);
-                    }
-                    if (mResolvedSwipeDirection == SWIPE_DIRECTION_LEFT
-                            ? realX < mStartX - mScaledTouchSlop
-                            : realX > mStartX + mScaledTouchSlop) {
-                        mTouchSlopReached = true;
-                    }
-                    view.setTranslationX(mResolvedSwipeDirection == SWIPE_DIRECTION_LEFT
-                                    ? Math.max(realX - mStartX, -mDragDistance)
-                                    : Math.min(realX - mStartX, mDragDistance));
-                    break;
-                case MotionEvent.ACTION_UP:
-                    if (mResolvedSwipeDirection == SWIPE_DIRECTION_LEFT
-                            ? realX < mStartX - mDragDistance
-                            : realX > mStartX + mDragDistance) {
-                        onActionPerformed();
-                        view.animate().x(mResolvedSwipeDirection == SWIPE_DIRECTION_LEFT
-                                ? -view.getWidth()
-                                : ((View) view.getParent()).getWidth() + view.getWidth())
-                                .setInterpolator(new AccelerateInterpolator(2f)).withEndAction(
-                                new Runnable() {
-                                    @Override
-                                    public void run() {
-                                        view.setTranslationX(0);
-                                    }
-                                });
-                        mSkipCancelAnimation = true;
-                    }
-                    if (mResolvedSwipeDirection == SWIPE_DIRECTION_LEFT
-                            ? realX < mStartX - mScaledTouchSlop
-                            : realX > mStartX + mScaledTouchSlop) {
-                        mTouchSlopReached = true;
-                    }
-                    if (!mTouchSlopReached) {
-                        mSkipCancelAnimation = true;
-                        view.animate().translationX(mResolvedSwipeDirection == SWIPE_DIRECTION_LEFT
-                                ? -mDragDistance / 2
-                                : mDragDistance / 2).
-                                setInterpolator(new DecelerateInterpolator()).withEndAction(
-                                new Runnable() {
-                                    @Override
-                                    public void run() {
-                                        view.animate().translationX(0).
-                                                setInterpolator(new AccelerateInterpolator());
-                                    }
-                                });
-                    }
-                case MotionEvent.ACTION_CANCEL:
-                    view.setPressed(false);
-                    if (!mSkipCancelAnimation) {
-                        view.animate().translationX(0)
-                                .setInterpolator(new AccelerateInterpolator(2f));
-                    }
-                    break;
-            }
-            return true;
-        }
-    };
-
-    public interface AffordanceListener {
-
-        /**
-         * Called when the view would like to report user activity.
-         *
-         * @param when The timestamp of the user activity in {@link SystemClock#uptimeMillis} time
-         *             base.
-         */
-        void onUserActivity(long when);
-
-        /**
-         * Called when the action of the affordance has been performed.
-         */
-        void onActionPerformed(SwipeAffordanceView view);
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java
index 5e2d06b..cf56fa57 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java
@@ -27,6 +27,7 @@
     boolean animateZ;
     boolean animateScale;
     boolean animateHeight;
+    boolean animateTopInset;
     boolean animateDimmed;
     boolean hasDelays;
 
@@ -60,6 +61,11 @@
         return this;
     }
 
+    public AnimationFilter animateTopInset() {
+        animateTopInset = true;
+        return this;
+    }
+
     public AnimationFilter animateDimmed() {
         animateDimmed = true;
         return this;
@@ -84,6 +90,7 @@
         animateZ |= filter.animateZ;
         animateScale |= filter.animateScale;
         animateHeight |= filter.animateHeight;
+        animateTopInset |= filter.animateTopInset;
         animateDimmed |= filter.animateDimmed;
         hasDelays |= filter.hasDelays;
     }
@@ -94,6 +101,7 @@
         animateZ = false;
         animateScale = false;
         animateHeight = false;
+        animateTopInset = false;
         animateDimmed = false;
         hasDelays = false;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
index 079b184..58176b9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -1599,6 +1599,7 @@
                 new AnimationFilter()
                         .animateAlpha()
                         .animateHeight()
+                        .animateTopInset()
                         .animateY()
                         .animateZ()
                         .hasDelays(),
@@ -1607,6 +1608,7 @@
                 new AnimationFilter()
                         .animateAlpha()
                         .animateHeight()
+                        .animateTopInset()
                         .animateY()
                         .animateZ()
                         .hasDelays(),
@@ -1615,6 +1617,7 @@
                 new AnimationFilter()
                         .animateAlpha()
                         .animateHeight()
+                        .animateTopInset()
                         .animateY()
                         .animateZ()
                         .hasDelays(),
@@ -1623,6 +1626,7 @@
                 new AnimationFilter()
                         .animateAlpha()
                         .animateHeight()
+                        .animateTopInset()
                         .animateY()
                         .animateDimmed()
                         .animateScale()
@@ -1651,6 +1655,7 @@
                 new AnimationFilter()
                         .animateAlpha()
                         .animateHeight()
+                        .animateTopInset()
                         .animateY()
                         .animateZ()
         };
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
index bd2541a..2b52c7e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
@@ -49,6 +49,7 @@
     private int mBottomStackPeekSize;
     private int mZDistanceBetweenElements;
     private int mZBasicHeight;
+    private int mRoundedRectCornerRadius;
 
     private StackIndentationFunctor mTopStackIndentationFunctor;
     private StackIndentationFunctor mBottomStackIndentationFunctor;
@@ -111,6 +112,8 @@
         mZBasicHeight = (MAX_ITEMS_IN_BOTTOM_STACK + 1) * mZDistanceBetweenElements;
         mBottomStackSlowDownLength = context.getResources()
                 .getDimensionPixelSize(R.dimen.bottom_stack_slow_down_length);
+        mRoundedRectCornerRadius = context.getResources().getDimensionPixelSize(
+                com.android.internal.R.dimen.notification_quantum_rounded_rect_radius);
     }
 
 
@@ -146,6 +149,67 @@
 
         handleDraggedViews(ambientState, resultState, algorithmState);
         updateDimmedActivated(ambientState, resultState, algorithmState);
+        updateClipping(resultState, algorithmState);
+    }
+
+    private void updateClipping(StackScrollState resultState,
+            StackScrollAlgorithmState algorithmState) {
+        float previousNotificationEnd = 0;
+        float previousNotificationStart = 0;
+        boolean previousNotificationIsSwiped = false;
+        int childCount = algorithmState.visibleChildren.size();
+        for (int i = 0; i < childCount; i++) {
+            ExpandableView child = algorithmState.visibleChildren.get(i);
+            StackScrollState.ViewState state = resultState.getViewStateForView(child);
+            float newYTranslation = state.yTranslation;
+            int newHeight = state.height;
+            // apply clipping and shadow
+            float newNotificationEnd = newYTranslation + newHeight;
+
+            // In the unlocked shade we have to clip a little bit higher because of the rounded
+            // corners of the notifications.
+            float clippingCorrection = state.dimmed ? 0 : mRoundedRectCornerRadius;
+
+            // When the previous notification is swiped, we don't clip the content to the
+            // bottom of it.
+            float clipHeight = previousNotificationIsSwiped
+                    ? newHeight
+                    : newNotificationEnd - (previousNotificationEnd - clippingCorrection);
+
+            updateChildClippingAndBackground(state, newHeight, clipHeight,
+                    (int) (newHeight - (previousNotificationStart - newYTranslation)));
+
+            if (!child.isTransparent()) {
+                // Only update the previous values if we are not transparent,
+                // otherwise we would clip to a transparent view.
+                previousNotificationStart = newYTranslation + child.getClipTopAmount();
+                previousNotificationEnd = newNotificationEnd;
+                previousNotificationIsSwiped = child.getTranslationX() != 0;
+            }
+        }
+    }
+
+    /**
+     * Updates the shadow outline and the clipping for a view.
+     *
+     * @param state the viewState to update
+     * @param realHeight the currently applied height of the view
+     * @param clipHeight the desired clip height, the rest of the view will be clipped from the top
+     * @param backgroundHeight the desired background height. The shadows of the view will be
+     *                         based on this height and the content will be clipped from the top
+     */
+    private void updateChildClippingAndBackground(StackScrollState.ViewState state, int realHeight,
+            float clipHeight, int backgroundHeight) {
+        if (realHeight > clipHeight) {
+            state.topOverLap = (int) (realHeight - clipHeight);
+        } else {
+            state.topOverLap = 0;
+        }
+        if (realHeight > backgroundHeight) {
+            state.clipTopAmount = (realHeight - backgroundHeight);
+        } else {
+            state.clipTopAmount = 0;
+        }
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java
index 44e10be..94cb16d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java
@@ -16,7 +16,6 @@
 
 package com.android.systemui.statusbar.stack;
 
-import android.graphics.Outline;
 import android.graphics.Rect;
 import android.util.Log;
 import android.view.View;
@@ -37,15 +36,12 @@
     private static final String CHILD_NOT_FOUND_TAG = "StackScrollStateNoSuchChild";
 
     private final ViewGroup mHostView;
-    private final int mRoundedRectCornerRadius;
     private Map<ExpandableView, ViewState> mStateMap;
     private final Rect mClipRect = new Rect();
 
     public StackScrollState(ViewGroup hostView) {
         mHostView = hostView;
         mStateMap = new HashMap<ExpandableView, ViewState>();
-        mRoundedRectCornerRadius = mHostView.getResources().getDimensionPixelSize(
-                com.android.internal.R.dimen.notification_quantum_rounded_rect_radius);
     }
 
     public ViewGroup getHostView() {
@@ -83,9 +79,6 @@
      */
     public void apply() {
         int numChildren = mHostView.getChildCount();
-        float previousNotificationEnd = 0;
-        float previousNotificationStart = 0;
-        boolean previousNotificationIsSwiped = false;
         for (int i = 0; i < numChildren; i++) {
             ExpandableView child = (ExpandableView) mHostView.getChildAt(i);
             ViewState state = mStateMap.get(child);
@@ -155,39 +148,41 @@
                 // apply dimming
                 child.setDimmed(state.dimmed, false /* animate */);
 
-                // apply clipping and shadow
-                float newNotificationEnd = newYTranslation + newHeight;
+                float oldClipTopAmount = child.getClipTopAmount();
+                if (oldClipTopAmount != state.clipTopAmount) {
+                    child.setClipTopAmount(state.clipTopAmount);
+                }
 
-                // In the unlocked shade we have to clip a little bit higher because of the rounded
-                // corners of the notifications.
-                float clippingCorrection = state.dimmed ? 0 : mRoundedRectCornerRadius;
-
-                // When the previous notification is swiped, we don't clip the content to the
-                // bottom of it.
-                float clipHeight = previousNotificationIsSwiped
-                        ? newHeight
-                        : newNotificationEnd - (previousNotificationEnd - clippingCorrection);
-
-                updateChildClippingAndBackground(child, newHeight,
-                        clipHeight,
-                        (int) (newHeight - (previousNotificationStart - newYTranslation)));
-
-                if (!child.isTransparent()) {
-                    // Only update the previous values if we are not transparent,
-                    // otherwise we would clip to a transparent view.
-                    previousNotificationStart = newYTranslation + child.getClipTopAmount();
-                    previousNotificationEnd = newNotificationEnd;
-                    previousNotificationIsSwiped = child.getTranslationX() != 0;
+                if (state.topOverLap != 0) {
+                    updateChildClip(child, newHeight, state.topOverLap);
+                } else {
+                    child.setClipBounds(null);
                 }
 
                 if(child instanceof SpeedBumpView) {
-                    performSpeedBumpAnimation(i, (SpeedBumpView) child, newNotificationEnd,
+                    float speedBumpEnd = newYTranslation + newHeight;
+                    performSpeedBumpAnimation(i, (SpeedBumpView) child, speedBumpEnd,
                             newYTranslation);
                 }
             }
         }
     }
 
+    /**
+     * Updates the clipping of a view
+     *
+     * @param child the view to update
+     * @param height the currently applied height of the view
+     * @param clipInset how much should this view be clipped from the top
+     */
+    private void updateChildClip(View child, int height, int clipInset) {
+        mClipRect.set(0,
+                clipInset,
+                child.getWidth(),
+                height);
+        child.setClipBounds(mClipRect);
+    }
+
     private void performSpeedBumpAnimation(int i, SpeedBumpView speedBump, float speedBumpEnd,
             float speedBumpStart) {
         View nextChild = getNextChildNotGone(i);
@@ -216,45 +211,6 @@
         return null;
     }
 
-    /**
-     * Updates the shadow outline and the clipping for a view.
-     *
-     * @param child the view to update
-     * @param realHeight the currently applied height of the view
-     * @param clipHeight the desired clip height, the rest of the view will be clipped from the top
-     * @param backgroundHeight the desired background height. The shadows of the view will be
-     *                         based on this height and the content will be clipped from the top
-     */
-    private void updateChildClippingAndBackground(ExpandableView child, int realHeight,
-            float clipHeight, int backgroundHeight) {
-        if (realHeight > clipHeight) {
-            updateChildClip(child, realHeight, clipHeight);
-        } else {
-            child.setClipBounds(null);
-        }
-        if (realHeight > backgroundHeight) {
-            child.setClipTopAmount(realHeight - backgroundHeight);
-        } else {
-            child.setClipTopAmount(0);
-        }
-    }
-
-    /**
-     * Updates the clipping of a view
-     *
-     * @param child the view to update
-     * @param height the currently applied height of the view
-     * @param clipHeight the desired clip height, the rest of the view will be clipped from the top
-     */
-    private void updateChildClip(View child, int height, float clipHeight) {
-        int clipInset = (int) (height - clipHeight);
-        mClipRect.set(0,
-                clipInset,
-                child.getWidth(),
-                height);
-        child.setClipBounds(mClipRect);
-    }
-
     public static class ViewState {
 
         // These are flags such that we can create masks for filtering.
@@ -276,6 +232,18 @@
         boolean dimmed;
 
         /**
+         * The amount which the view should be clipped from the top. This is calculated to
+         * perceive consistent shadows.
+         */
+        int clipTopAmount;
+
+        /**
+         * How much does the child overlap with the previous view on the top? Can be used for
+         * a clipping optimization
+         */
+        int topOverLap;
+
+        /**
          * The index of the view, only accounting for views not equal to GONE
          */
         int notGoneIndex;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
index f019e6c..f41ab3a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
@@ -133,10 +133,11 @@
         boolean scaleChanging = child.getScaleX() != viewState.scale;
         boolean alphaChanging = alpha != child.getAlpha();
         boolean heightChanging = viewState.height != child.getActualHeight();
+        boolean topInsetChanging = viewState.clipTopAmount != child.getClipTopAmount();
         boolean wasAdded = mNewAddChildren.contains(child);
         boolean hasDelays = mAnimationFilter.hasDelays;
         boolean isDelayRelevant = yTranslationChanging || zTranslationChanging || scaleChanging ||
-                alphaChanging || heightChanging;
+                alphaChanging || heightChanging || topInsetChanging;
         long delay = 0;
         if (hasDelays && isDelayRelevant || wasAdded) {
             delay = calculateChildAnimationDelay(viewState, finalState);
@@ -167,6 +168,11 @@
             startHeightAnimation(child, viewState, delay);
         }
 
+        // start top inset animation
+        if (topInsetChanging) {
+            startInsetAnimation(child, viewState, delay);
+        }
+
         // start dimmed animation
         child.setDimmed(viewState.dimmed, mAnimationFilter.animateDimmed);
 
@@ -280,6 +286,64 @@
         child.setTag(TAG_END_HEIGHT, newEndValue);
     }
 
+    private void startInsetAnimation(final ExpandableView child,
+            StackScrollState.ViewState viewState, long delay) {
+        Integer previousStartValue = getChildTag(child, TAG_START_TOP_INSET);
+        Integer previousEndValue = getChildTag(child, TAG_END_TOP_INSET);
+        int newEndValue = viewState.clipTopAmount;
+        if (previousEndValue != null && previousEndValue == newEndValue) {
+            return;
+        }
+        ValueAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_TOP_INSET);
+        if (!mAnimationFilter.animateTopInset) {
+            // just a local update was performed
+            if (previousAnimator != null) {
+                // we need to increase all animation keyframes of the previous animator by the
+                // relative change to the end value
+                PropertyValuesHolder[] values = previousAnimator.getValues();
+                int relativeDiff = newEndValue - previousEndValue;
+                int newStartValue = previousStartValue + relativeDiff;
+                values[0].setIntValues(newStartValue, newEndValue);
+                child.setTag(TAG_START_TOP_INSET, newStartValue);
+                child.setTag(TAG_END_TOP_INSET, newEndValue);
+                previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime());
+                return;
+            } else {
+                // no new animation needed, let's just apply the value
+                child.setClipTopAmount(newEndValue);
+                return;
+            }
+        }
+
+        ValueAnimator animator = ValueAnimator.ofInt(child.getClipTopAmount(), newEndValue);
+        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+            @Override
+            public void onAnimationUpdate(ValueAnimator animation) {
+                child.setClipTopAmount((int) animation.getAnimatedValue());
+            }
+        });
+        animator.setInterpolator(mFastOutSlowInInterpolator);
+        long newDuration = cancelAnimatorAndGetNewDuration(previousAnimator);
+        animator.setDuration(newDuration);
+        if (delay > 0 && (previousAnimator == null || !previousAnimator.isRunning())) {
+            animator.setStartDelay(delay);
+        }
+        animator.addListener(getGlobalAnimationFinishedListener());
+        // remove the tag when the animation is finished
+        animator.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                child.setTag(TAG_ANIMATOR_TOP_INSET, null);
+                child.setTag(TAG_START_TOP_INSET, null);
+                child.setTag(TAG_END_TOP_INSET, null);
+            }
+        });
+        startAnimator(animator);
+        child.setTag(TAG_ANIMATOR_TOP_INSET, animator);
+        child.setTag(TAG_START_TOP_INSET, child.getClipTopAmount());
+        child.setTag(TAG_END_TOP_INSET, newEndValue);
+    }
+
     private void startAlphaAnimation(final ExpandableView child,
             final StackScrollState.ViewState viewState, long delay) {
         Float previousStartValue = getChildTag(child,TAG_START_ALPHA);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
index 25147b4..c2bd1cb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
@@ -17,12 +17,12 @@
 package com.android.systemui.statusbar.tv;
 
 import android.os.IBinder;
+import android.service.notification.NotificationListenerService.Ranking;
 import android.service.notification.StatusBarNotification;
 import android.view.View;
 import android.view.ViewGroup.LayoutParams;
 import android.view.WindowManager;
 
-import com.android.internal.policy.IKeyguardShowCallback;
 import com.android.internal.statusbar.StatusBarIcon;
 import com.android.systemui.statusbar.BaseStatusBar;
 
@@ -50,7 +50,7 @@
     }
 
     @Override
-    public void addNotificationInternal(StatusBarNotification notification) {
+    public void addNotificationInternal(StatusBarNotification notification, Ranking ranking) {
     }
 
     @Override
@@ -58,7 +58,7 @@
     }
 
     @Override
-    protected void removeNotificationInternal(String key) {
+    protected void removeNotificationInternal(String key, Ranking ranking) {
     }
 
     @Override
@@ -117,7 +117,7 @@
     }
 
     @Override
-    protected void updateNotificationIcons() {
+    protected void updateNotifications() {
     }
 
     @Override
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index e178773..1e21e4f 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -66,6 +66,7 @@
 import android.provider.Settings;
 import android.service.dreams.DreamService;
 import android.service.dreams.IDreamManager;
+import android.telephony.TelephonyManager;
 import android.util.DisplayMetrics;
 import android.util.EventLog;
 import android.util.Log;
@@ -1925,9 +1926,8 @@
                 ServiceManager.checkService(DreamService.DREAM_SERVICE));
     }
 
-    static ITelephony getTelephonyService() {
-        return ITelephony.Stub.asInterface(
-                ServiceManager.checkService(Context.TELEPHONY_SERVICE));
+    TelephonyManager getTelephonyService() {
+        return (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
     }
 
     static IAudioService getAudioService() {
@@ -2010,14 +2010,10 @@
                 // If an incoming call is ringing, HOME is totally disabled.
                 // (The user is already on the InCallScreen at this point,
                 // and his ONLY options are to answer or reject the call.)
-                try {
-                    ITelephony telephonyService = getTelephonyService();
-                    if (telephonyService != null && telephonyService.isRinging()) {
-                        Log.i(TAG, "Ignoring HOME; there's a ringing incoming call.");
-                        return -1;
-                    }
-                } catch (RemoteException ex) {
-                    Log.w(TAG, "RemoteException from getPhoneInterface()", ex);
+                TelephonyManager telephonyManager = getTelephonyService();
+                if (telephonyManager != null && telephonyManager.isRinging()) {
+                    Log.i(TAG, "Ignoring HOME; there's a ringing incoming call.");
+                    return -1;
                 }
 
                 // Delay handling home if a double-tap is possible.
@@ -3967,37 +3963,33 @@
                     }
                 }
                 if (down) {
-                    ITelephony telephonyService = getTelephonyService();
-                    if (telephonyService != null) {
-                        try {
-                            if (telephonyService.isRinging()) {
-                                // If an incoming call is ringing, either VOLUME key means
-                                // "silence ringer".  We handle these keys here, rather than
-                                // in the InCallScreen, to make sure we'll respond to them
-                                // even if the InCallScreen hasn't come to the foreground yet.
-                                // Look for the DOWN event here, to agree with the "fallback"
-                                // behavior in the InCallScreen.
-                                Log.i(TAG, "interceptKeyBeforeQueueing:"
-                                      + " VOLUME key-down while ringing: Silence ringer!");
+                    TelephonyManager telephonyManager = getTelephonyService();
+                    if (telephonyManager != null) {
+                        if (telephonyManager.isRinging()) {
+                            // If an incoming call is ringing, either VOLUME key means
+                            // "silence ringer".  We handle these keys here, rather than
+                            // in the InCallScreen, to make sure we'll respond to them
+                            // even if the InCallScreen hasn't come to the foreground yet.
+                            // Look for the DOWN event here, to agree with the "fallback"
+                            // behavior in the InCallScreen.
+                            Log.i(TAG, "interceptKeyBeforeQueueing:"
+                                  + " VOLUME key-down while ringing: Silence ringer!");
 
-                                // Silence the ringer.  (It's safe to call this
-                                // even if the ringer has already been silenced.)
-                                telephonyService.silenceRinger();
+                            // Silence the ringer.  (It's safe to call this
+                            // even if the ringer has already been silenced.)
+                            telephonyManager.silenceRinger();
 
-                                // And *don't* pass this key thru to the current activity
-                                // (which is probably the InCallScreen.)
-                                result &= ~ACTION_PASS_TO_USER;
-                                break;
-                            }
-                            if (telephonyService.isOffhook()
-                                    && (result & ACTION_PASS_TO_USER) == 0) {
-                                // If we are in call but we decided not to pass the key to
-                                // the application, handle the volume change here.
-                                handleVolumeKey(AudioManager.STREAM_VOICE_CALL, keyCode);
-                                break;
-                            }
-                        } catch (RemoteException ex) {
-                            Log.w(TAG, "ITelephony threw RemoteException", ex);
+                            // And *don't* pass this key thru to the current activity
+                            // (which is probably the InCallScreen.)
+                            result &= ~ACTION_PASS_TO_USER;
+                            break;
+                        }
+                        if (telephonyManager.isOffhook()
+                                && (result & ACTION_PASS_TO_USER) == 0) {
+                            // If we are in call but we decided not to pass the key to
+                            // the application, handle the volume change here.
+                            handleVolumeKey(AudioManager.STREAM_VOICE_CALL, keyCode);
+                            break;
                         }
                     }
 
@@ -4014,14 +4006,10 @@
             case KeyEvent.KEYCODE_ENDCALL: {
                 result &= ~ACTION_PASS_TO_USER;
                 if (down) {
-                    ITelephony telephonyService = getTelephonyService();
+                    TelephonyManager telephonyManager = getTelephonyService();
                     boolean hungUp = false;
-                    if (telephonyService != null) {
-                        try {
-                            hungUp = telephonyService.endCall();
-                        } catch (RemoteException ex) {
-                            Log.w(TAG, "ITelephony threw RemoteException", ex);
-                        }
+                    if (telephonyManager != null) {
+                        hungUp = telephonyManager.endCall();
                     }
                     interceptPowerKeyDown(!interactive || hungUp);
                 } else {
@@ -4057,23 +4045,19 @@
                         interceptScreenshotChord();
                     }
 
-                    ITelephony telephonyService = getTelephonyService();
+                    TelephonyManager telephonyManager = getTelephonyService();
                     boolean hungUp = false;
-                    if (telephonyService != null) {
-                        try {
-                            if (telephonyService.isRinging()) {
-                                // Pressing Power while there's a ringing incoming
-                                // call should silence the ringer.
-                                telephonyService.silenceRinger();
-                            } else if ((mIncallPowerBehavior
-                                    & Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_HANGUP) != 0
-                                    && telephonyService.isOffhook() && interactive) {
-                                // Otherwise, if "Power button ends call" is enabled,
-                                // the Power button will hang up any current active call.
-                                hungUp = telephonyService.endCall();
-                            }
-                        } catch (RemoteException ex) {
-                            Log.w(TAG, "ITelephony threw RemoteException", ex);
+                    if (telephonyManager != null) {
+                        if (telephonyManager.isRinging()) {
+                            // Pressing Power while there's a ringing incoming
+                            // call should silence the ringer.
+                            telephonyManager.silenceRinger();
+                        } else if ((mIncallPowerBehavior
+                                & Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_HANGUP) != 0
+                                && telephonyManager.isOffhook() && interactive) {
+                            // Otherwise, if "Power button ends call" is enabled,
+                            // the Power button will hang up any current active call.
+                            hungUp = telephonyManager.endCall();
                         }
                     }
                     interceptPowerKeyDown(!interactive || hungUp
@@ -4106,16 +4090,12 @@
             case KeyEvent.KEYCODE_MEDIA_PAUSE:
             case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
                 if (down) {
-                    ITelephony telephonyService = getTelephonyService();
-                    if (telephonyService != null) {
-                        try {
-                            if (!telephonyService.isIdle()) {
-                                // Suppress PLAY/PAUSE toggle when phone is ringing or in-call
-                                // to avoid music playback.
-                                break;
-                            }
-                        } catch (RemoteException ex) {
-                            Log.w(TAG, "ITelephony threw RemoteException", ex);
+                    TelephonyManager telephonyManager = getTelephonyService();
+                    if (telephonyManager != null) {
+                        if (!telephonyManager.isIdle()) {
+                            // Suppress PLAY/PAUSE toggle when phone is ringing or in-call
+                            // to avoid music playback.
+                            break;
                         }
                     }
                 }
@@ -4145,20 +4125,16 @@
 
             case KeyEvent.KEYCODE_CALL: {
                 if (down) {
-                    ITelephony telephonyService = getTelephonyService();
-                    if (telephonyService != null) {
-                        try {
-                            if (telephonyService.isRinging()) {
-                                Log.i(TAG, "interceptKeyBeforeQueueing:"
-                                      + " CALL key-down while ringing: Answer the call!");
-                                telephonyService.answerRingingCall();
+                    TelephonyManager telephonyManager = getTelephonyService();
+                    if (telephonyManager != null) {
+                        if (telephonyManager.isRinging()) {
+                            Log.i(TAG, "interceptKeyBeforeQueueing:"
+                                  + " CALL key-down while ringing: Answer the call!");
+                            telephonyManager.answerRingingCall();
 
-                                // And *don't* pass this key thru to the current activity
-                                // (which is presumably the InCallScreen.)
-                                result &= ~ACTION_PASS_TO_USER;
-                            }
-                        } catch (RemoteException ex) {
-                            Log.w(TAG, "ITelephony threw RemoteException", ex);
+                            // And *don't* pass this key thru to the current activity
+                            // (which is presumably the InCallScreen.)
+                            result &= ~ACTION_PASS_TO_USER;
                         }
                     }
                 }
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index b94ea62..1b1fc8b 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -259,13 +259,17 @@
                     userIds[i]));
         }
 
-        ManagedServiceInfo[] toRemove = new ManagedServiceInfo[mServices.size()];
+        ArrayList<ManagedServiceInfo> toRemove = new ArrayList<ManagedServiceInfo>();
         final SparseArray<ArrayList<ComponentName>> toAdd
                 = new SparseArray<ArrayList<ComponentName>>();
 
         synchronized (mMutex) {
-            // unbind and remove all existing services
-            toRemove = mServices.toArray(toRemove);
+            // Unbind automatically bound services, retain system services.
+            for (ManagedServiceInfo service : mServices) {
+                if (!service.isSystem) {
+                    toRemove.add(service);
+                }
+            }
 
             final ArraySet<ComponentName> newEnabled = new ArraySet<ComponentName>();
             final ArraySet<String> newPackages = new ArraySet<String>();
diff --git a/services/core/java/com/android/server/task/TaskManagerService.java b/services/core/java/com/android/server/task/TaskManagerService.java
index 6d208ff..80030b4 100644
--- a/services/core/java/com/android/server/task/TaskManagerService.java
+++ b/services/core/java/com/android/server/task/TaskManagerService.java
@@ -16,12 +16,22 @@
 
 package com.android.server.task;
 
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+import android.app.task.ITaskManager;
+import android.app.task.Task;
 import android.content.Context;
-import android.content.Task;
+import android.content.pm.PackageManager;
+import android.os.Binder;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
-import android.util.Log;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.util.Slog;
 import android.util.SparseArray;
 
 import com.android.server.task.controllers.TaskStatus;
@@ -39,13 +49,6 @@
     /** Master list of tasks. */
     private final TaskStore mTasks;
 
-    /** Check the pending queue and start any tasks. */
-    static final int MSG_RUN_PENDING = 0;
-    /** Initiate the stop task flow. */
-    static final int MSG_STOP_TASK = 1;
-    /** */
-    static final int MSG_CHECK_TASKS = 2;
-
     /**
      * Track Services that have currently active or pending tasks. The index is provided by
      * {@link TaskStatus#getServiceToken()}
@@ -54,6 +57,14 @@
             new SparseArray<TaskServiceContext>();
 
     private final TaskHandler mHandler;
+    private final TaskManagerStub mTaskManagerStub;
+
+    /** Check the pending queue and start any tasks. */
+    static final int MSG_RUN_PENDING = 0;
+    /** Initiate the stop task flow. */
+    static final int MSG_STOP_TASK = 1;
+    /** */
+    static final int MSG_CHECK_TASKS = 2;
 
     private class TaskHandler extends Handler {
 
@@ -94,21 +105,6 @@
     }
 
     /**
-     * Entry point from client to schedule the provided task.
-     * This will add the task to the
-     * @param task Task object containing execution parameters
-     * @param userId The id of the user this task is for.
-     * @param uId The package identifier of the application this task is for.
-     * @param canPersistTask Whether or not the client has the appropriate permissions for persisting
-     *                    of this task.
-     * @return Result of this operation. See <code>TaskManager#RESULT_*</code> return codes.
-     */
-    public int schedule(Task task, int userId, int uId, boolean canPersistTask) {
-        TaskStatus taskStatus = mTasks.addNewTaskForUser(task, userId, uId, canPersistTask);
-        return 0;
-    }
-
-    /**
      * Initializes the system service.
      * <p>
      * Subclasses must define a single argument constructor that accepts the context
@@ -121,11 +117,42 @@
         super(context);
         mTasks = new TaskStore(context);
         mHandler = new TaskHandler(context.getMainLooper());
+        mTaskManagerStub = new TaskManagerStub();
     }
 
     @Override
     public void onStart() {
+        publishBinderService(Context.TASK_SERVICE, mTaskManagerStub);
+    }
 
+    /**
+     * Entry point from client to schedule the provided task.
+     * This will add the task to the
+     * @param task Task object containing execution parameters
+     * @param userId The id of the user this task is for.
+     * @param uId The package identifier of the application this task is for.
+     * @param canPersistTask Whether or not the client has the appropriate permissions for
+     *     persisting of this task.
+     * @return Result of this operation. See <code>TaskManager#RESULT_*</code> return codes.
+     */
+    public int schedule(Task task, int userId, int uId, boolean canPersistTask) {
+        TaskStatus taskStatus = mTasks.addNewTaskForUser(task, userId, uId, canPersistTask);
+        return 0;
+    }
+
+    public List<Task> getPendingTasks(int uid) {
+        ArrayList<Task> outList = new ArrayList<Task>(3);
+        synchronized (mTasks) {
+            final SparseArray<TaskStatus> tasks = mTasks.getTasks();
+            final int N = tasks.size();
+            for (int i = 0; i < N; i++) {
+                TaskStatus ts = tasks.get(i);
+                if (ts.getUid() == uid) {
+                    outList.add(ts.getTask());
+                }
+            }
+        }
+        return outList;
     }
 
     // StateChangedListener implementations.
@@ -162,7 +189,7 @@
     public void onTaskCompleted(int serviceToken, int taskId, boolean needsReschedule) {
         final TaskServiceContext serviceContext = mActiveServices.get(serviceToken);
         if (serviceContext == null) {
-            Log.e(TAG, "Task completed for invalid service context; " + serviceToken);
+            Slog.e(TAG, "Task completed for invalid service context; " + serviceToken);
             return;
         }
 
@@ -203,4 +230,98 @@
     private void postCheckTasksMessage() {
         mHandler.obtainMessage(MSG_CHECK_TASKS).sendToTarget();
     }
+
+    /**
+     * Binder stub trampoline implementation
+     */
+    final class TaskManagerStub extends ITaskManager.Stub {
+        /** Cache determination of whether a given app can persist tasks
+         * key is uid of the calling app; value is undetermined/true/false
+         */
+        private final SparseArray<Boolean> mPersistCache = new SparseArray<Boolean>();
+
+        // Determine whether the caller is allowed to persist tasks, with a small cache
+        // because the lookup is expensive enough that we'd like to avoid repeating it.
+        // This must be called from within the calling app's binder identity!
+        private boolean canCallerPersistTasks() {
+            final boolean canPersist;
+            final int callingUid = Binder.getCallingUid();
+            synchronized (mPersistCache) {
+                Boolean cached = mPersistCache.get(callingUid);
+                if (cached) {
+                    canPersist = cached.booleanValue();
+                } else {
+                    // Persisting tasks is tantamount to running at boot, so we permit
+                    // it when the app has declared that it uses the RECEIVE_BOOT_COMPLETED
+                    // permission
+                    int result = getContext().checkCallingPermission(
+                            android.Manifest.permission.RECEIVE_BOOT_COMPLETED);
+                    canPersist = (result == PackageManager.PERMISSION_GRANTED);
+                    mPersistCache.put(callingUid, canPersist);
+                }
+            }
+            return canPersist;
+        }
+
+        // ITaskManager implementation
+        @Override
+        public int schedule(Task task) throws RemoteException {
+            final boolean canPersist = canCallerPersistTasks();
+            final int uid = Binder.getCallingUid();
+            final int userId = UserHandle.getCallingUserId();
+
+            long ident = Binder.clearCallingIdentity();
+            try {
+                return TaskManagerService.this.schedule(task, userId, uid, canPersist);
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
+
+        @Override
+        public List<Task> getAllPendingTasks() throws RemoteException {
+            return null;
+        }
+
+        @Override
+        public void cancelAll() throws RemoteException {
+        }
+
+        @Override
+        public void cancel(int taskId) throws RemoteException {
+        }
+
+        /**
+         * "dumpsys" infrastructure
+         */
+        @Override
+        public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+            getContext().enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);
+
+            long identityToken = Binder.clearCallingIdentity();
+            try {
+                TaskManagerService.this.dumpInternal(pw);
+            } finally {
+                Binder.restoreCallingIdentity(identityToken);
+            }
+        }
+    };
+
+    void dumpInternal(PrintWriter pw) {
+        synchronized (mTasks) {
+            pw.print("Registered tasks:");
+            if (mTasks.size() > 0) {
+                SparseArray<TaskStatus> tasks = mTasks.getTasks();
+                for (int i = 0; i < tasks.size(); i++) {
+                    TaskStatus ts = tasks.get(i);
+                    pw.println();
+                    ts.dump(pw, "  ");
+                }
+            } else {
+                pw.println();
+                pw.println("No tasks scheduled.");
+            }
+        }
+        pw.println();
+    }
 }
diff --git a/services/core/java/com/android/server/task/TaskServiceContext.java b/services/core/java/com/android/server/task/TaskServiceContext.java
index b51cbb3..165445a 100644
--- a/services/core/java/com/android/server/task/TaskServiceContext.java
+++ b/services/core/java/com/android/server/task/TaskServiceContext.java
@@ -45,7 +45,7 @@
  * is reused to start concurrent tasks on the TaskService. Information here is unique
  * to the service.
  * Functionality provided by this class:
- *     - Managages wakelock for the service.
+ *     - Manages wakelock for the service.
  *     - Sends onStartTask() and onStopTask() messages to client app, and handles callbacks.
  *     -
  */
diff --git a/services/core/java/com/android/server/task/TaskStore.java b/services/core/java/com/android/server/task/TaskStore.java
index 3bfc8a5..81187c8 100644
--- a/services/core/java/com/android/server/task/TaskStore.java
+++ b/services/core/java/com/android/server/task/TaskStore.java
@@ -16,8 +16,8 @@
 
 package com.android.server.task;
 
+import android.app.task.Task;
 import android.content.Context;
-import android.content.Task;
 import android.util.SparseArray;
 
 import com.android.server.task.controllers.TaskStatus;
@@ -95,6 +95,13 @@
     }
 
     /**
+     * @return the number of tasks in the store
+     */
+    public int size() {
+        return mTasks.size();
+    }
+
+    /**
      * @return The live array of TaskStatus objects.
      */
     public SparseArray<TaskStatus> getTasks() {
diff --git a/services/core/java/com/android/server/task/controllers/ConnectivityController.java b/services/core/java/com/android/server/task/controllers/ConnectivityController.java
index 6a4e1f3..a0038c5d 100644
--- a/services/core/java/com/android/server/task/controllers/ConnectivityController.java
+++ b/services/core/java/com/android/server/task/controllers/ConnectivityController.java
@@ -70,13 +70,10 @@
     }
 
     /**
-     * @param userId Id of the user for whom we are updating the connectivity state.
+     *
      */
-    private void updateTrackedTasks(int userId) {
+    private void updateTrackedTasks() {
         for (TaskStatus ts : mTrackedTasks) {
-            if (ts.userId != userId) {
-                continue;
-            }
             boolean prevIsConnected = ts.connectivityConstraintSatisfied.getAndSet(mConnectivity);
             boolean prevIsMetered = ts.meteredConstraintSatisfied.getAndSet(mMetered);
             if (prevIsConnected != mConnectivity || prevIsMetered != mMetered) {
@@ -107,14 +104,13 @@
                 final NetworkInfo activeNetwork = connManager.getActiveNetworkInfo();
                 // This broadcast gets sent a lot, only update if the active network has changed.
                 if (activeNetwork.getType() == networkType) {
-                    final int userid = context.getUserId();
                     mMetered = false;
                     mConnectivity =
                             !intent.getBooleanExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, false);
                     if (mConnectivity) {  // No point making the call if we know there's no conn.
                         mMetered = connManager.isActiveNetworkMetered();
                     }
-                    updateTrackedTasks(userid);
+                    updateTrackedTasks();
                 }
             } else {
                 Log.w(TAG, "Unrecognised action in intent: " + action);
diff --git a/services/core/java/com/android/server/task/controllers/TaskStatus.java b/services/core/java/com/android/server/task/controllers/TaskStatus.java
index d96fedc..d270016 100644
--- a/services/core/java/com/android/server/task/controllers/TaskStatus.java
+++ b/services/core/java/com/android/server/task/controllers/TaskStatus.java
@@ -16,17 +16,19 @@
 
 package com.android.server.task.controllers;
 
+import android.app.task.Task;
 import android.content.ComponentName;
-import android.content.Task;
 import android.content.pm.PackageParser;
 import android.os.Bundle;
 import android.os.SystemClock;
+import android.os.UserHandle;
 
+import java.io.PrintWriter;
 import java.util.concurrent.atomic.AtomicBoolean;
 
 /**
  * Uniquely identifies a task internally.
- * Created from the public {@link android.content.Task} object when it lands on the scheduler.
+ * Created from the public {@link android.app.task.Task} object when it lands on the scheduler.
  * Contains current state of the requirements of the task, as well as a function to evaluate
  * whether it's ready to run.
  * This object is shared among the various controllers - hence why the different fields are atomic.
@@ -36,10 +38,9 @@
  * @hide
  */
 public class TaskStatus {
+    final Task task;
     final int taskId;
-    final int userId;
     final int uId;
-    final ComponentName component;
     final Bundle extras;
 
     final AtomicBoolean chargingConstraintSatisfied = new AtomicBoolean();
@@ -57,9 +58,9 @@
     private long earliestRunTimeElapsedMillis;
     private long latestRunTimeElapsedMillis;
 
-    /** Provide a unique handle to the service that this task will be run on. */
+    /** Provide a handle to the service that this task will be run on. */
     public int getServiceToken() {
-        return component.hashCode() + userId;
+        return uId;
     }
 
     /** Generate a TaskStatus object for a given task and uid. */
@@ -70,9 +71,8 @@
 
     /** Set up the state of a newly scheduled task. */
     TaskStatus(Task task, int userId, int uId) {
+        this.task = task;
         this.taskId = task.getTaskId();
-        this.userId = userId;
-        this.component = task.getService();
         this.extras = task.getExtras();
         this.uId = uId;
 
@@ -100,16 +100,20 @@
         hasConnectivityConstraint = task.getNetworkCapabilities() == Task.NetworkType.ANY;
     }
 
+    public Task getTask() {
+        return task;
+    }
+
     public int getTaskId() {
         return taskId;
     }
 
     public ComponentName getServiceComponent() {
-        return component;
+        return task.getService();
     }
 
     public int getUserId() {
-        return userId;
+        return UserHandle.getUserId(uId);
     }
 
     public int getUid() {
@@ -161,9 +165,9 @@
 
     @Override
     public int hashCode() {
-        int result = component.hashCode();
+        int result = getServiceComponent().hashCode();
         result = 31 * result + taskId;
-        result = 31 * result + userId;
+        result = 31 * result + uId;
         return result;
     }
 
@@ -174,7 +178,14 @@
 
         TaskStatus that = (TaskStatus) o;
         return ((taskId == that.taskId)
-                && (userId == that.userId)
-                && (component.equals(that.component)));
+                && (uId == that.uId)
+                && (getServiceComponent().equals(that.getServiceComponent())));
+    }
+
+    // Dumpsys infrastructure
+    public void dump(PrintWriter pw, String prefix) {
+        pw.print(prefix); pw.print("Task "); pw.println(taskId);
+        pw.print(prefix); pw.print("uid="); pw.println(uId);
+        pw.print(prefix); pw.print("component="); pw.println(task.getService());
     }
 }
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index de46b16..bc34e0ef 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -83,6 +83,7 @@
 import com.android.server.search.SearchManagerService;
 import com.android.server.statusbar.StatusBarManagerService;
 import com.android.server.storage.DeviceStorageMonitorService;
+import com.android.server.task.TaskManagerService;
 import com.android.server.trust.TrustManagerService;
 import com.android.server.tv.TvInputManagerService;
 import com.android.server.twilight.TwilightService;
@@ -132,6 +133,8 @@
             "com.android.server.hdmi.HdmiCecService";
     private static final String ETHERNET_SERVICE_CLASS =
             "com.android.server.ethernet.EthernetService";
+    private static final String TASK_SERVICE_CLASS =
+            "com.android.server.task.TaskManagerService";
 
     private final int mFactoryTestMode;
     private Timer mProfilerSnapshotTimer;
@@ -831,6 +834,8 @@
 
             mSystemServiceManager.startService(UiModeManagerService.class);
 
+            mSystemServiceManager.startService(TaskManagerService.class);
+
             if (!disableNonCoreServices) {
                 try {
                     if (pm.hasSystemFeature(PackageManager.FEATURE_BACKUP)) {
diff --git a/telecomm/java/com/android/internal/telecomm/ITelecommService.aidl b/telecomm/java/com/android/internal/telecomm/ITelecommService.aidl
new file mode 100644
index 0000000..c439211
--- /dev/null
+++ b/telecomm/java/com/android/internal/telecomm/ITelecommService.aidl
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telecomm;
+
+/**
+ * Interface used to interact with Telecomm. Mostly this is used by TelephonyManager for passing
+ * commands that were previously handled by ITelephony.
+ * {@hide}
+ */
+oneway interface ITelecommService {
+
+    /**
+     * Silence the ringer if an incoming call is currently ringing.
+     * (If vibrating, stop the vibrator also.)
+     *
+     * It's safe to call this if the ringer has already been silenced, or
+     * even if there's no incoming call.  (If so, this method will do nothing.)
+     */
+    void silenceRinger();
+}
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index a896861..4aed1fe 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -30,6 +30,7 @@
 import android.telephony.Rlog;
 import android.util.Log;
 
+import com.android.internal.telecomm.ITelecommService;
 import com.android.internal.telephony.IPhoneSubInfo;
 import com.android.internal.telephony.ITelephony;
 import com.android.internal.telephony.ITelephonyRegistry;
@@ -65,6 +66,8 @@
 public class TelephonyManager {
     private static final String TAG = "TelephonyManager";
 
+    private static final String TELECOMM_SERVICE_NAME = "telecomm";
+
     private static ITelephonyRegistry sRegistry;
 
     /**
@@ -1536,6 +1539,10 @@
         return ITelephony.Stub.asInterface(ServiceManager.getService(Context.TELEPHONY_SERVICE));
     }
 
+    private ITelecommService getTelecommService() {
+        return ITelecommService.Stub.asInterface(ServiceManager.getService(TELECOMM_SERVICE_NAME));
+    }
+
     //
     //
     // PhoneStateListener
@@ -2016,9 +2023,9 @@
     @PrivateApi
     public void silenceRinger() {
         try {
-            getITelephony().silenceRinger();
+            getTelecommService().silenceRinger();
         } catch (RemoteException e) {
-            Log.e(TAG, "Error calling ITelephony#silenceRinger", e);
+            Log.e(TAG, "Error calling ITelecommService#silenceRinger", e);
         }
     }
 
diff --git a/tools/layoutlib/bridge/src/android/content/res/Resources_Theme_Delegate.java b/tools/layoutlib/bridge/src/android/content/res/Resources_Theme_Delegate.java
index 31d1594..f4a9f52 100644
--- a/tools/layoutlib/bridge/src/android/content/res/Resources_Theme_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/content/res/Resources_Theme_Delegate.java
@@ -97,6 +97,13 @@
         return found;
     }
 
+    @LayoutlibDelegate
+    /*package*/ static TypedArray resolveAttributes(Resources thisResources, Theme thisTheme,
+            int[] values, int[] attrs) {
+        // FIXME
+        return null;
+    }
+
     // ---- private helper methods ----
 
     private static boolean setupResources(Theme thisTheme) {
diff --git a/tools/layoutlib/bridge/src/android/content/res/TypedArray_Delegate.java b/tools/layoutlib/bridge/src/android/content/res/TypedArray_Delegate.java
index 0a7899a..5d89f83 100644
--- a/tools/layoutlib/bridge/src/android/content/res/TypedArray_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/content/res/TypedArray_Delegate.java
@@ -27,4 +27,10 @@
         // pass
         return false;
     }
+
+    @LayoutlibDelegate
+    /*package*/ static TypedArray obtain(Resources res, int len) {
+        // FIXME
+        return null;
+    }
 }
diff --git a/tools/layoutlib/bridge/src/android/graphics/BidiRenderer.java b/tools/layoutlib/bridge/src/android/graphics/BidiRenderer.java
index 802cf1c..33813d1 100644
--- a/tools/layoutlib/bridge/src/android/graphics/BidiRenderer.java
+++ b/tools/layoutlib/bridge/src/android/graphics/BidiRenderer.java
@@ -18,9 +18,11 @@
 
 import java.awt.Font;
 import java.awt.Graphics2D;
+import java.awt.Toolkit;
 import java.awt.font.FontRenderContext;
 import java.awt.font.GlyphVector;
 import java.awt.geom.Rectangle2D;
+import java.util.ArrayList;
 import java.util.LinkedList;
 import java.util.List;
 
@@ -36,12 +38,12 @@
 @SuppressWarnings("deprecation")
 public class BidiRenderer {
 
-    /* package */ static class ScriptRun {
+    /*package*/ static class ScriptRun {
         int start;
         int limit;
         boolean isRtl;
         int scriptCode;
-        FontInfo font;
+        Font font;
 
         public ScriptRun(int start, int limit, boolean isRtl) {
             this.start = start;
@@ -51,9 +53,10 @@
         }
     }
 
-    private Graphics2D mGraphics;
-    private Paint_Delegate mPaint;
+    private final Graphics2D mGraphics;
+    private final Paint_Delegate mPaint;
     private char[] mText;
+    private List<Font> mFonts;
     // Bounds of the text drawn so far.
     private RectF mBounds;
     private float mBaseline;
@@ -63,11 +66,15 @@
      * @param paint The Paint to use to get the fonts. Should not be null.
      * @param text Unidirectional text. Should not be null.
      */
-    /* package */ BidiRenderer(Graphics2D graphics, Paint_Delegate paint, char[] text) {
+    /*package*/ BidiRenderer(Graphics2D graphics, Paint_Delegate paint, char[] text) {
         assert (paint != null);
         mGraphics = graphics;
         mPaint = paint;
         mText = text;
+        mFonts = new ArrayList<Font>(paint.getFonts().size());
+        for (FontInfo fontInfo : paint.getFonts()) {
+            mFonts.add(fontInfo.mFont);
+        }
     }
 
     /**
@@ -94,7 +101,7 @@
         // the script runs.
         mBounds = new RectF(x, y, x, y);
         mBaseline = y;
-        for (ScriptRun run : getScriptRuns(mText, start, limit, isRtl, mPaint.getFonts())) {
+        for (ScriptRun run : getScriptRuns(mText, start, limit, isRtl, mFonts)) {
             int flag = Font.LAYOUT_NO_LIMIT_CONTEXT | Font.LAYOUT_NO_START_CONTEXT;
             flag |= isRtl ? Font.LAYOUT_RIGHT_TO_LEFT : Font.LAYOUT_LEFT_TO_RIGHT;
             renderScript(run.start, run.limit, run.font, flag, advances, advancesIndex, draw);
@@ -108,16 +115,15 @@
      * much as possible. This also implements a fallback mechanism to render characters that cannot
      * be drawn using the preferred font.
      */
-    private void renderScript(int start, int limit, FontInfo preferredFont, int flag,
+    private void renderScript(int start, int limit, Font preferredFont, int flag,
             float[] advances, int advancesIndex, boolean draw) {
-        List<FontInfo> fonts = mPaint.getFonts();
-        if (fonts == null || preferredFont == null) {
+        if (mFonts.size() == 0 || preferredFont == null) {
             return;
         }
 
         while (start < limit) {
             boolean foundFont = false;
-            int canDisplayUpTo = preferredFont.mFont.canDisplayUpTo(mText, start, limit);
+            int canDisplayUpTo = preferredFont.canDisplayUpTo(mText, start, limit);
             if (canDisplayUpTo == -1) {
                 // We can draw all characters in the text.
                 render(start, limit, preferredFont, flag, advances, advancesIndex, draw);
@@ -133,8 +139,8 @@
             // The current character cannot be drawn with the preferred font. Cycle through all the
             // fonts to check which one can draw it.
             int charCount = Character.isHighSurrogate(mText[start]) ? 2 : 1;
-            for (FontInfo font : fonts) {
-                canDisplayUpTo = font.mFont.canDisplayUpTo(mText, start, start + charCount);
+            for (Font font : mFonts) {
+                canDisplayUpTo = font.canDisplayUpTo(mText, start, start + charCount);
                 if (canDisplayUpTo == -1) {
                     render(start, start+charCount, font, flag, advances, advancesIndex, draw);
                     start += charCount;
@@ -160,15 +166,19 @@
      * Renders the text to the right of the bounds with the given font.
      * @param font The font to render the text with.
      */
-    private void render(int start, int limit, FontInfo font, int flag, float[] advances,
+    private void render(int start, int limit, Font font, int flag, float[] advances,
             int advancesIndex, boolean draw) {
 
-        // Since the metrics don't have anti-aliasing set, we create a new FontRenderContext with
-        // the anti-aliasing set.
-        FontRenderContext f = font.mMetrics.getFontRenderContext();
-        FontRenderContext frc = new FontRenderContext(f.getTransform(), mPaint.isAntiAliased(),
-                f.usesFractionalMetrics());
-        GlyphVector gv = font.mFont.layoutGlyphVector(frc, mText, start, limit, flag);
+        FontRenderContext frc;
+        if (mGraphics != null) {
+            frc = mGraphics.getFontRenderContext();
+        } else {
+            frc = Toolkit.getDefaultToolkit().getFontMetrics(font).getFontRenderContext();
+            // Metrics obtained this way don't have anti-aliasing set. So,
+            // we create a new FontRenderContext with anti-aliasing set.
+            frc = new FontRenderContext(font.getTransform(), mPaint.isAntiAliased(), frc.usesFractionalMetrics());
+        }
+        GlyphVector gv = font.layoutGlyphVector(frc, mText, start, limit, flag);
         int ng = gv.getNumGlyphs();
         int[] ci = gv.getGlyphCharIndices(0, ng, null);
         if (advances != null) {
@@ -206,7 +216,7 @@
     }
 
     /* package */  static List<ScriptRun> getScriptRuns(char[] text, int start, int limit,
-            boolean isRtl, List<FontInfo> fonts) {
+            boolean isRtl, List<Font> fonts) {
         LinkedList<ScriptRun> scriptRuns = new LinkedList<ScriptRun>();
 
         int count = limit - start;
@@ -225,10 +235,10 @@
 
     // TODO: Replace this method with one which returns the font based on the scriptCode.
     private static void setScriptFont(char[] text, ScriptRun run,
-            List<FontInfo> fonts) {
-        for (FontInfo fontInfo : fonts) {
-            if (fontInfo.mFont.canDisplayUpTo(text, run.start, run.limit) == -1) {
-                run.font = fontInfo;
+            List<Font> fonts) {
+        for (Font font : fonts) {
+            if (font.canDisplayUpTo(text, run.start, run.limit) == -1) {
+                run.font = font;
                 return;
             }
         }
diff --git a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
index 56c0de9..e9daffd 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
@@ -979,7 +979,6 @@
             final float startX, final float startY, final int flags, long paint,
             long typeface) {
 
-        // TODO: use typeface.
         draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
                 new GcSnapshot.Drawable() {
             @Override
@@ -1097,7 +1096,7 @@
     /**
      * Executes a {@link GcSnapshot.Drawable} with a given canvas and paint.
      * <p>Note that the drawable may actually be executed several times if there are
-     * layers involved (see {@link #saveLayer(RectF, int, int)}.
+     * layers involved (see {@link #saveLayer(RectF, Paint_Delegate, int)}.
      */
     private static void draw(long nCanvas, long nPaint, boolean compositeOnly, boolean forceSrcMode,
             GcSnapshot.Drawable drawable) {
@@ -1117,7 +1116,7 @@
      * Executes a {@link GcSnapshot.Drawable} with a given canvas. No paint object will be provided
      * to {@link GcSnapshot.Drawable#draw(Graphics2D, Paint_Delegate)}.
      * <p>Note that the drawable may actually be executed several times if there are
-     * layers involved (see {@link #saveLayer(RectF, int, int)}.
+     * layers involved (see {@link #saveLayer(RectF, Paint_Delegate, int)}.
      */
     private static void draw(long nCanvas, GcSnapshot.Drawable drawable) {
         // get the delegate from the native int.
@@ -1190,12 +1189,6 @@
         return mSnapshot.clipRect(left, top, right, bottom, regionOp);
     }
 
-    private void setBitmap(Bitmap_Delegate bitmap) {
-        mBitmap = bitmap;
-        assert mSnapshot.size() == 1;
-        mSnapshot.setBitmap(mBitmap);
-    }
-
     private static void drawBitmap(
             long nativeCanvas,
             Bitmap_Delegate bitmap,
diff --git a/tools/layoutlib/bridge/src/android/graphics/FontFamily_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/FontFamily_Delegate.java
new file mode 100644
index 0000000..5e7543a0
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/graphics/FontFamily_Delegate.java
@@ -0,0 +1,225 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics;
+
+import com.android.ide.common.rendering.api.LayoutLog;
+import com.android.layoutlib.bridge.Bridge;
+import com.android.layoutlib.bridge.impl.DelegateManager;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+import java.awt.Font;
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+import static android.graphics.Typeface_Delegate.SYSTEM_FONTS;
+
+/**
+ * Delegate implementing the native methods of android.graphics.FontFamily
+ *
+ * Through the layoutlib_create tool, the original native methods of FontFamily have been replaced
+ * by calls to methods of the same name in this delegate class.
+ *
+ * This class behaves like the original native implementation, but in Java, keeping previously
+ * native data into its own objects and mapping them to int that are sent back and forth between
+ * it and the original FontFamily class.
+ *
+ * @see DelegateManager
+ */
+public class FontFamily_Delegate {
+
+    // FONT_SUFFIX_ITALIC will always match FONT_SUFFIX_BOLDITALIC and hence it must be checked
+    // separately.
+    private static final String FONT_SUFFIX_BOLDITALIC = "BoldItalic.ttf";
+    private static final String FONT_SUFFIX_BOLD = "Bold.ttf";
+    private static final String FONT_SUFFIX_ITALIC = "Italic.ttf";
+    private static final String FONT_SUBSTRING_COMPACT = "UI";
+
+    /**
+     * A class associating {@link Font} with its metadata.
+     */
+    private static final class FontInfo {
+        Font mFont;
+        /** Regular, Bold, Italic, or BoldItalic. */
+        int mStyle;
+        /**
+         * The variant of the Font - compact or elegant.
+         * @see Paint#setElegantTextHeight(boolean)
+         */
+        boolean mIsCompact;
+    }
+
+    // ---- delegate manager ----
+    private static final DelegateManager<FontFamily_Delegate> sManager =
+            new DelegateManager<FontFamily_Delegate>(FontFamily_Delegate.class);
+
+    // ---- delegate helper data ----
+    private static String sFontLocation;
+    private static final List<FontFamily_Delegate> sPostInitDelegate = new
+            ArrayList<FontFamily_Delegate>();
+
+
+    // ---- delegate data ----
+    private List<FontInfo> mFonts = new ArrayList<FontInfo>();
+    // Path of fonts that haven't been created since sFontLoader hasn't been initialized.
+    private List<String> mPath = new ArrayList<String>();
+
+
+    // ---- Public Helper methods ----
+
+    public static FontFamily_Delegate getDelegate(long nativeFontFamily) {
+        return sManager.getDelegate(nativeFontFamily);
+    }
+
+    public static synchronized void setFontLocation(String fontLocation) {
+        sFontLocation = fontLocation;
+        for (FontFamily_Delegate fontFamily : sPostInitDelegate) {
+            fontFamily.init();
+        }
+        sPostInitDelegate.clear();
+    }
+
+    public Font getFont(int style, boolean isCompact) {
+        FontInfo plainFont = null;
+        FontInfo styledFont = null;  // Font matching the style but not isCompact
+        for (FontInfo font : mFonts) {
+            if (font.mStyle == style) {
+                if (font.mIsCompact == isCompact) {
+                    return font.mFont;
+                }
+                styledFont = font;
+            }
+            if (font.mStyle == Font.PLAIN) {
+                if (plainFont == null) {
+                    plainFont = font;
+                    continue;
+                }
+                if (font.mIsCompact == isCompact) {
+                    // Override the previous selection of plain font since we've found a better one.
+                    plainFont = font;
+                }
+            }
+        }
+        if (styledFont != null) {
+            return styledFont.mFont;
+        }
+
+        // No font with the mentioned style is found. Try to derive one.
+        if (plainFont != null && style > 0 && style < 4) {
+            styledFont = new FontInfo();
+            styledFont.mFont = plainFont.mFont.deriveFont(style);
+            styledFont.mStyle = style;
+            styledFont.mIsCompact = plainFont.mIsCompact;
+            // Add the font to the list of fonts so that we don't have to derive it the next time.
+            mFonts.add(styledFont);
+            return styledFont.mFont;
+        }
+        return null;
+    }
+
+    // ---- native methods ----
+
+    @LayoutlibDelegate
+    /*package*/ static long nCreateFamily() {
+        FontFamily_Delegate delegate = new FontFamily_Delegate();
+        if (sFontLocation != null) {
+            delegate.init();
+        } else {
+            sPostInitDelegate.add(delegate);
+        }
+        return sManager.addNewDelegate(delegate);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void nUnrefFamily(long nativePtr) {
+        sManager.removeJavaReferenceFor(nativePtr);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static boolean nAddFont(long nativeFamily, String path) {
+        FontFamily_Delegate delegate = getDelegate(nativeFamily);
+        if (delegate != null) {
+            if (sFontLocation == null) {
+                delegate.mPath.add(path);
+                return true;
+            }
+            return delegate.addFont(path);
+        }
+        return false;
+    }
+
+    private void init() {
+        for (String path : mPath) {
+            addFont(path);
+        }
+        mPath = null;
+    }
+
+    private boolean addFont(String path) {
+        Font font = loadFont(path);
+        if (font == null) {
+            return false;
+        }
+        FontInfo fontInfo = new FontInfo();
+        fontInfo.mFont = font;
+        addFontMetadata(fontInfo, path);
+        // TODO ensure that mFonts doesn't have the font with this style already.
+        mFonts.add(fontInfo);
+        return true;
+    }
+
+    private static void addFontMetadata(FontInfo fontInfo, String path) {
+        int style = Font.PLAIN;
+        String fontName = path.substring(path.lastIndexOf('/'), path.length());
+        if (fontName.endsWith(FONT_SUFFIX_BOLDITALIC)) {
+            style = Font.BOLD | Font.ITALIC;
+        } else if (fontName.endsWith(FONT_SUFFIX_BOLD)) {
+            style = Font.BOLD;
+        } else if (fontName.endsWith(FONT_SUFFIX_ITALIC)) {
+            style = Font.ITALIC;
+        }
+        fontInfo.mStyle = style;
+
+        // Names of compact fonts end with UI-<style>.ttf. For example, NotoNakshUI-Regular.ttf.
+        // This should go away when this info is passed on by nAddFont().
+        int hyphenIndex = fontName.lastIndexOf('-');
+        fontInfo.mIsCompact = hyphenIndex > 0 &&
+                fontName.substring(0, hyphenIndex).endsWith(FONT_SUBSTRING_COMPACT);
+
+    }
+
+    private static Font loadFont(String path) {
+        if (path.startsWith(SYSTEM_FONTS) ) {
+            String relativePath = path.substring(SYSTEM_FONTS.length());
+            File f = new File(sFontLocation, relativePath);
+
+            try {
+                return Font.createFont(Font.TRUETYPE_FONT, f);
+            } catch (Exception e) {
+                Bridge.getLog().fidelityWarning(LayoutLog.TAG_BROKEN,
+                        String.format("Unable to load font %1$s", relativePath),
+                        null /*throwable*/, null /*data*/);
+            }
+        } else {
+            Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
+                    "Only platform fonts located in " + SYSTEM_FONTS + "can be loaded.",
+                    null /*throwable*/, null /*data*/);
+        }
+
+        return null;
+    }
+}
diff --git a/tools/layoutlib/bridge/src/android/graphics/Matrix_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Matrix_Delegate.java
index 8862f5b..f42f48f 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Matrix_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Matrix_Delegate.java
@@ -203,6 +203,16 @@
     }
 
     @LayoutlibDelegate
+    /*package*/ static boolean native_isAffine(long native_object) {
+        Matrix_Delegate d = sManager.getDelegate(native_object);
+        if (d == null) {
+            return true;
+        }
+
+        return (d.computeTypeMask() & kPerspective_Mask) == 0;
+    }
+
+    @LayoutlibDelegate
     /*package*/ static boolean native_rectStaysRect(long native_object) {
         Matrix_Delegate d = sManager.getDelegate(native_object);
         if (d == null) {
diff --git a/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java
index 83df745..911f4e7 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java
@@ -53,7 +53,7 @@
 public class Paint_Delegate {
 
     /**
-     * Class associating a {@link Font} and it's {@link java.awt.FontMetrics}.
+     * Class associating a {@link Font} and its {@link java.awt.FontMetrics}.
      */
     /*package*/ static final class FontInfo {
         Font mFont;
@@ -66,8 +66,6 @@
 
     // ---- delegate helper data ----
     private List<FontInfo> mFonts;
-    private final FontRenderContext mFontContext = new FontRenderContext(
-            new AffineTransform(), true, true);
 
     // ---- delegate data ----
     private int mFlags;
@@ -83,6 +81,7 @@
     private float mTextScaleX;
     private float mTextSkewX;
     private int mHintingMode = Paint.HINTING_ON;
+    private boolean mIsCompact = true;
 
     private Xfermode_Delegate mXfermode;
     private ColorFilter_Delegate mColorFilter;
@@ -101,8 +100,7 @@
     }
 
     /**
-     * Returns the list of {@link Font} objects. The first item is the main font, the rest
-     * are fall backs for characters not present in the main font.
+     * Returns the list of {@link Font} objects.
      */
     public List<FontInfo> getFonts() {
         return mFonts;
@@ -437,12 +435,20 @@
 
     @LayoutlibDelegate
     /*package*/ static boolean isElegantTextHeight(Paint thisPaint) {
-        return false;
+        // get the delegate from the native int.
+        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
+        return delegate != null && !delegate.mIsCompact;
     }
 
     @LayoutlibDelegate
     /*package*/ static void setElegantTextHeight(Paint thisPaint, boolean elegant) {
-        // TODO
+        // get the delegate from the native int.
+        Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
+        if (delegate == null) {
+            return;
+        }
+
+        delegate.mIsCompact = !elegant;
     }
 
     @LayoutlibDelegate
@@ -621,7 +627,6 @@
         int inc = count > 0 ? 1 : -1;
 
         int measureIndex = 0;
-        float measureAcc = 0;
         for (int i = index; i != index + count; i += inc, measureIndex++) {
             int start, end;
             if (i < index) {
@@ -640,7 +645,6 @@
                 measuredWidth[measureIndex] = res;
             }
 
-            measureAcc += res;
             if (res > maxWidth) {
                 // we should not return this char index, but since it's 0-based
                 // and we need to return a count, we simply return measureIndex;
@@ -818,7 +822,7 @@
             return filter;
         }
 
-        delegate.mColorFilter = ColorFilter_Delegate.getDelegate(filter);;
+        delegate.mColorFilter = ColorFilter_Delegate.getDelegate(filter);
 
         // since none of those are supported, display a fidelity warning right away
         if (delegate.mColorFilter != null && delegate.mColorFilter.isSupported() == false) {
@@ -940,52 +944,17 @@
     }
 
     @LayoutlibDelegate
-    /*package*/ static int native_getTextWidths(long native_object, char[] text, int index,
-            int count, int bidiFlags, float[] widths) {
-        // get the delegate from the native int.
-        Paint_Delegate delegate = sManager.getDelegate(native_object);
-        if (delegate == null) {
-            return 0;
-        }
-
-        if (delegate.mFonts.size() > 0) {
-            // FIXME: handle multi-char characters (see measureText)
-            float totalAdvance = 0;
-            for (int i = 0; i < count; i++) {
-                char c = text[i + index];
-                boolean found = false;
-                for (FontInfo info : delegate.mFonts) {
-                    if (info.mFont.canDisplay(c)) {
-                        float adv = info.mMetrics.charWidth(c);
-                        totalAdvance += adv;
-                        if (widths != null) {
-                            widths[i] = adv;
-                        }
-
-                        found = true;
-                        break;
-                    }
-                }
-
-                if (found == false) {
-                    // no advance for this char.
-                    if (widths != null) {
-                        widths[i] = 0.f;
-                    }
-                }
-            }
-
-            return (int) totalAdvance;
-        }
-
-        return 0;
+    /*package*/ static int native_getTextWidths(long native_object, long native_typeface,
+            char[] text, int index, int count, int bidiFlags, float[] widths) {
+        return (int) native_getTextRunAdvances(native_object, native_typeface, text, index, count,
+                index, count, bidiFlags, widths, 0);
     }
 
     @LayoutlibDelegate
-    /*package*/ static int native_getTextWidths(long native_object, String text, int start,
-            int end, int bidiFlags, float[] widths) {
-        return native_getTextWidths(native_object, text.toCharArray(), start, end - start,
-                bidiFlags, widths);
+    /*package*/ static int native_getTextWidths(long native_object, long native_typeface,
+            String text, int start, int end, int bidiFlags, float[] widths) {
+        return native_getTextWidths(native_object, native_typeface, text.toCharArray(), start,
+                end - start, bidiFlags, widths);
     }
 
     @LayoutlibDelegate
@@ -997,15 +966,20 @@
 
     @LayoutlibDelegate
     /*package*/ static float native_getTextRunAdvances(long native_object,
+            long native_typeface /*ignored*/,
             char[] text, int index, int count, int contextIndex, int contextCount,
             int flags, float[] advances, int advancesIndex) {
 
+        // native_typeface is passed here since Framework's old implementation did not have the
+        // typeface object associated with the Paint. Since, we follow the new framework way,
+        // we store the typeface with the paint and use it directly.
+
         if (advances != null)
             for (int i = advancesIndex; i< advancesIndex+count; i++)
                 advances[i]=0;
         // get the delegate from the native int.
         Paint_Delegate delegate = sManager.getDelegate(native_object);
-        if (delegate == null || delegate.mFonts == null || delegate.mFonts.size() == 0) {
+        if (delegate == null) {
             return 0.f;
         }
         boolean isRtl = isRtl(flags);
@@ -1017,7 +991,7 @@
     }
 
     @LayoutlibDelegate
-    /*package*/ static float native_getTextRunAdvances(long native_object,
+    /*package*/ static float native_getTextRunAdvances(long native_object, long native_typeface,
             String text, int start, int end, int contextStart, int contextEnd,
             int flags, float[] advances, int advancesIndex) {
         // FIXME: support contextStart and contextEnd
@@ -1025,8 +999,8 @@
         char[] buffer = TemporaryBuffer.obtain(count);
         TextUtils.getChars(text, start, end, buffer, 0);
 
-        return native_getTextRunAdvances(native_object, buffer, 0, count, contextStart,
-                contextEnd - contextStart, flags, advances, advancesIndex);
+        return native_getTextRunAdvances(native_object, native_typeface, buffer, 0, count,
+                contextStart, contextEnd - contextStart, flags, advances, advancesIndex);
     }
 
     @LayoutlibDelegate
@@ -1076,7 +1050,7 @@
 
         // get the delegate from the native int.
         Paint_Delegate delegate = sManager.getDelegate(nativePaint);
-        if (delegate == null || delegate.mFonts == null || delegate.mFonts.size() == 0) {
+        if (delegate == null) {
             return;
         }
         delegate.measureText(text, index, count, isRtl(bidiFlags)).roundOut(bounds);
@@ -1150,7 +1124,7 @@
     private void updateFontObject() {
         if (mTypeface != null) {
             // Get the fonts from the TypeFace object.
-            List<Font> fonts = mTypeface.getFonts();
+            List<Font> fonts = mTypeface.getFonts(mIsCompact);
 
             // create new font objects as well as FontMetrics, based on the current text size
             // and skew info.
diff --git a/tools/layoutlib/bridge/src/android/graphics/Typeface_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Typeface_Delegate.java
index 60cd157..ed8f3b4 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Typeface_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Typeface_Delegate.java
@@ -19,7 +19,6 @@
 import com.android.ide.common.rendering.api.LayoutLog;
 import com.android.layoutlib.bridge.Bridge;
 import com.android.layoutlib.bridge.impl.DelegateManager;
-import com.android.layoutlib.bridge.impl.FontLoader;
 import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
 
 import android.content.res.AssetManager;
@@ -44,100 +43,67 @@
  */
 public final class Typeface_Delegate {
 
-    private static final String SYSTEM_FONTS = "/system/fonts/";
+    public static final String SYSTEM_FONTS = "/system/fonts/";
 
     // ---- delegate manager ----
     private static final DelegateManager<Typeface_Delegate> sManager =
             new DelegateManager<Typeface_Delegate>(Typeface_Delegate.class);
 
     // ---- delegate helper data ----
-    private static final String DEFAULT_FAMILY = "sans-serif";
-
-    private static FontLoader sFontLoader;
-    private static final List<Typeface_Delegate> sPostInitDelegate =
-            new ArrayList<Typeface_Delegate>();
+    private static String sFontLocation;
 
     // ---- delegate data ----
 
-    private final String mFamily;
+    private final long[] mFontFamilies;  // the reference to FontFamily_Delegate.
     private int mStyle;
-    private List<Font> mFonts;
 
+    private static long sDefaultTypeface;
 
     // ---- Public Helper methods ----
-
-    public static synchronized void init(FontLoader fontLoader) {
-        sFontLoader = fontLoader;
-
-        for (Typeface_Delegate delegate : sPostInitDelegate) {
-            delegate.init();
-        }
-        sPostInitDelegate.clear();
+    public static synchronized void setFontLocation(String fontLocation) {
+        sFontLocation = fontLocation;
+        FontFamily_Delegate.setFontLocation(fontLocation);
     }
 
     public static Typeface_Delegate getDelegate(long nativeTypeface) {
         return sManager.getDelegate(nativeTypeface);
     }
 
-    public static List<Font> getFonts(Typeface typeface) {
-        return getFonts(typeface.native_instance);
-    }
-
-    public static List<Font> getFonts(long native_int) {
-        Typeface_Delegate delegate = sManager.getDelegate(native_int);
-        if (delegate == null) {
-            return null;
+    public List<Font> getFonts(boolean compact) {
+        List<Font> fonts = new ArrayList<Font>(mFontFamilies.length);
+        for (long fontFamily : mFontFamilies) {
+            FontFamily_Delegate ffd = FontFamily_Delegate.getDelegate(fontFamily);
+            if (ffd != null) {
+                Font font = ffd.getFont(mStyle, compact);
+                if (font != null) {
+                    fonts.add(font);
+                }
+            }
         }
-
-        return delegate.getFonts();
-    }
-
-    public List<Font> getFonts() {
-        return mFonts;
+        return fonts;
     }
 
     // ---- native methods ----
 
     @LayoutlibDelegate
     /*package*/ static synchronized long nativeCreate(String familyName, int style) {
-        if (familyName == null) {
-            familyName = DEFAULT_FAMILY;
-        }
-        if (style < 0) {
-            style = Typeface.NORMAL;
-        }
-
-        Typeface_Delegate newDelegate = new Typeface_Delegate(familyName, style);
-        if (sFontLoader != null) {
-            newDelegate.init();
-        } else {
-            // font loader has not been initialized yet, add the delegate to a list of delegates
-            // to init when the font loader is initialized.
-            // There won't be any rendering before this happens anyway.
-            sPostInitDelegate.add(newDelegate);
-        }
-
-        return sManager.addNewDelegate(newDelegate);
+        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
+                "Could not find font with family \"" + familyName + "\".",
+                null /*throwable*/, null /*data*/);
+        return 0;
     }
 
     @LayoutlibDelegate
     /*package*/ static synchronized long nativeCreateFromTypeface(long native_instance, int style) {
         Typeface_Delegate delegate = sManager.getDelegate(native_instance);
         if (delegate == null) {
+            delegate = sManager.getDelegate(sDefaultTypeface);
+        }
+        if (delegate == null) {
             return 0;
         }
 
-        Typeface_Delegate newDelegate = new Typeface_Delegate(delegate.mFamily, style);
-        if (sFontLoader != null) {
-            newDelegate.init();
-        } else {
-            // font loader has not been initialized yet, add the delegate to a list of delegates
-            // to init when the font loader is initialized.
-            // There won't be any rendering before this happens anyway.
-            sPostInitDelegate.add(newDelegate);
-        }
-
-        return sManager.addNewDelegate(newDelegate);
+        return sManager.addNewDelegate(new Typeface_Delegate(delegate.mFontFamilies, style));
     }
 
     @LayoutlibDelegate
@@ -149,31 +115,15 @@
 
     @LayoutlibDelegate
     /*package*/ static synchronized long nativeCreateFromFile(String path) {
-        if (path.startsWith(SYSTEM_FONTS) ) {
-            String relativePath = path.substring(SYSTEM_FONTS.length());
-            File f = new File(sFontLoader.getOsFontsLocation(), relativePath);
+        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
+                "Typeface.createFromFile() is not supported.,", null, null);
+        return 0;
+    }
 
-            try {
-                Font font = Font.createFont(Font.TRUETYPE_FONT, f);
-                if (font != null) {
-                    Typeface_Delegate newDelegate = new Typeface_Delegate(font);
-                    return sManager.addNewDelegate(newDelegate);
-                }
-            } catch (Exception e) {
-                Bridge.getLog().fidelityWarning(LayoutLog.TAG_BROKEN,
-                        String.format("Unable to load font %1$s", relativePath),
-                            null /*throwable*/, null /*data*/);
-            }
-        } else {
-            Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
-                    "Typeface.createFromFile() can only work with platform fonts located in " +
-                        SYSTEM_FONTS,
-                    null /*throwable*/, null /*data*/);
-        }
-
-
-        // return a copy of the base font
-        return nativeCreate(null, 0);
+    @LayoutlibDelegate
+    /*package*/ static synchronized long nativeCreateFromArray(long[] familyArray) {
+        Typeface_Delegate delegate = new Typeface_Delegate(familyArray, Typeface.NORMAL);
+        return sManager.addNewDelegate(delegate);
     }
 
     @LayoutlibDelegate
@@ -191,24 +141,21 @@
         return delegate.mStyle;
     }
 
+    @LayoutlibDelegate
+    /*package*/ static void nativeSetDefault(long native_instance) {
+        sDefaultTypeface = native_instance;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static File getSystemFontConfigLocation() {
+        return new File(sFontLocation);
+    }
+
     // ---- Private delegate/helper methods ----
 
-    private Typeface_Delegate(String family, int style) {
-        mFamily = family;
+    private Typeface_Delegate(long[] fontFamilies, int style) {
+        mFontFamilies = fontFamilies;
         mStyle = style;
     }
 
-    private Typeface_Delegate(Font font) {
-        mFamily = font.getFamily();
-        mStyle = Typeface.NORMAL;
-
-        mFonts = sFontLoader.getFallbackFonts(mStyle);
-
-        // insert the font glyph first.
-        mFonts.add(0, font);
-    }
-
-    private void init() {
-        mFonts = sFontLoader.getFont(mFamily, mStyle);
-    }
 }
diff --git a/tools/layoutlib/bridge/src/android/util/Xml_Delegate.java b/tools/layoutlib/bridge/src/android/util/Xml_Delegate.java
new file mode 100644
index 0000000..a193330
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/util/Xml_Delegate.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.util;
+
+import com.android.layoutlib.bridge.impl.DelegateManager;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+import org.kxml2.io.KXmlParser;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+/**
+ * Delegate overriding some methods of android.util.Xml
+ *
+ * Through the layoutlib_create tool, the original methods of Xml have been replaced
+ * by calls to methods of the same name in this delegate class.
+ *
+ * Because it's a stateless class to start with, there's no need to keep a {@link DelegateManager}
+ * around to map int to instance of the delegate.
+ */
+public class Xml_Delegate {
+
+    @LayoutlibDelegate
+    /*package*/ static XmlPullParser newPullParser() {
+        try {
+            KXmlParser parser = new KXmlParser();
+            // The prebuilt kxml2 library with the IDE doesn't support DOCECL.
+//            parser.setFeature(XmlPullParser.FEATURE_PROCESS_DOCDECL, true);
+            parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
+            return parser;
+        } catch (XmlPullParserException e) {
+            throw new AssertionError();
+        }
+    }
+}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
index fa8050f..ffab4de 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
@@ -26,7 +26,6 @@
 import com.android.ide.common.rendering.api.Result;
 import com.android.ide.common.rendering.api.Result.Status;
 import com.android.ide.common.rendering.api.SessionParams;
-import com.android.layoutlib.bridge.impl.FontLoader;
 import com.android.layoutlib.bridge.impl.RenderDrawable;
 import com.android.layoutlib.bridge.impl.RenderSessionImpl;
 import com.android.layoutlib.bridge.util.DynamicIdMap;
@@ -61,7 +60,7 @@
 /**
  * Main entry point of the LayoutLib Bridge.
  * <p/>To use this bridge, simply instantiate an object of type {@link Bridge} and call
- * {@link #createScene(SceneParams)}
+ * {@link #createSession(SessionParams)}
  */
 public final class Bridge extends com.android.ide.common.rendering.api.Bridge {
 
@@ -147,8 +146,7 @@
             if (getClass() != obj.getClass()) return false;
 
             IntArray other = (IntArray) obj;
-            if (!Arrays.equals(mArray, other.mArray)) return false;
-            return true;
+            return Arrays.equals(mArray, other.mArray);
         }
     }
 
@@ -251,14 +249,7 @@
         }
 
         // load the fonts.
-        FontLoader fontLoader = FontLoader.create(fontLocation.getAbsolutePath());
-        if (fontLoader != null) {
-            Typeface_Delegate.init(fontLoader);
-        } else {
-            log.error(LayoutLog.TAG_BROKEN,
-                    "Failed create FontLoader in layout lib.", null);
-            return false;
-        }
+        Typeface_Delegate.setFontLocation(fontLocation.getAbsolutePath());
 
         // now parse com.android.internal.R (and only this one as android.R is a subset of
         // the internal version), and put the content in the maps.
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/FontLoader.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/FontLoader.java
deleted file mode 100644
index cc7338a..0000000
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/FontLoader.java
+++ /dev/null
@@ -1,398 +0,0 @@
-/*
- * 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.
- */
-
-package com.android.layoutlib.bridge.impl;
-
-import org.xml.sax.Attributes;
-import org.xml.sax.SAXException;
-import org.xml.sax.helpers.DefaultHandler;
-
-import android.graphics.Typeface;
-
-import java.awt.Font;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-import javax.xml.parsers.ParserConfigurationException;
-import javax.xml.parsers.SAXParser;
-import javax.xml.parsers.SAXParserFactory;
-
-/**
- * Provides {@link Font} object to the layout lib.
- * <p/>
- * The fonts are loaded from the SDK directory. Family/style mapping is done by parsing the
- * fonts.xml file located alongside the ttf files.
- */
-public final class FontLoader {
-    private static final String FONTS_SYSTEM = "system_fonts.xml";
-    private static final String FONTS_VENDOR = "vendor_fonts.xml";
-    private static final String FONTS_FALLBACK = "fallback_fonts.xml";
-
-    private static final String NODE_FAMILYSET = "familyset";
-    private static final String NODE_FAMILY = "family";
-    private static final String NODE_NAME = "name";
-    private static final String NODE_FILE = "file";
-
-    private static final String ATTRIBUTE_VARIANT = "variant";
-    private static final String ATTRIBUTE_VALUE_ELEGANT = "elegant";
-    private static final String FONT_SUFFIX_NONE = ".ttf";
-    private static final String FONT_SUFFIX_REGULAR = "-Regular.ttf";
-    private static final String FONT_SUFFIX_BOLD = "-Bold.ttf";
-    // FONT_SUFFIX_ITALIC will always match FONT_SUFFIX_BOLDITALIC and hence it must be checked
-    // separately.
-    private static final String FONT_SUFFIX_ITALIC = "Italic.ttf";
-    private static final String FONT_SUFFIX_BOLDITALIC = "-BoldItalic.ttf";
-
-    // This must match the values of Typeface styles so that we can use them for indices in this
-    // array.
-    private static final int[] AWT_STYLES = new int[] {
-        Font.PLAIN,
-        Font.BOLD,
-        Font.ITALIC,
-        Font.BOLD | Font.ITALIC
-    };
-    private static int[] DERIVE_BOLD_ITALIC = new int[] {
-        Typeface.ITALIC, Typeface.BOLD, Typeface.NORMAL
-    };
-    private static int[] DERIVE_ITALIC = new int[] { Typeface.NORMAL };
-    private static int[] DERIVE_BOLD = new int[] { Typeface.NORMAL };
-
-    private static final List<FontInfo> mMainFonts = new ArrayList<FontInfo>();
-    private static final List<FontInfo> mFallbackFonts = new ArrayList<FontInfo>();
-
-    private final String mOsFontsLocation;
-
-    public static FontLoader create(String fontOsLocation) {
-        try {
-            SAXParserFactory parserFactory = SAXParserFactory.newInstance();
-                parserFactory.setNamespaceAware(true);
-
-            // parse the system fonts
-            FontHandler handler = parseFontFile(parserFactory, fontOsLocation, FONTS_SYSTEM);
-            List<FontInfo> systemFonts = handler.getFontList();
-
-
-            // parse the fallback fonts
-            handler = parseFontFile(parserFactory, fontOsLocation, FONTS_FALLBACK);
-            List<FontInfo> fallbackFonts = handler.getFontList();
-
-            return new FontLoader(fontOsLocation, systemFonts, fallbackFonts);
-        } catch (ParserConfigurationException e) {
-            // return null below
-        } catch (SAXException e) {
-            // return null below
-        } catch (FileNotFoundException e) {
-            // return null below
-        } catch (IOException e) {
-            // return null below
-        }
-
-        return null;
-    }
-
-    private static FontHandler parseFontFile(SAXParserFactory parserFactory,
-            String fontOsLocation, String fontFileName)
-            throws ParserConfigurationException, SAXException, IOException, FileNotFoundException {
-
-        SAXParser parser = parserFactory.newSAXParser();
-        File f = new File(fontOsLocation, fontFileName);
-
-        FontHandler definitionParser = new FontHandler(
-                fontOsLocation + File.separator);
-        parser.parse(new FileInputStream(f), definitionParser);
-        return definitionParser;
-    }
-
-    private FontLoader(String fontOsLocation,
-            List<FontInfo> fontList, List<FontInfo> fallBackList) {
-        mOsFontsLocation = fontOsLocation;
-        mMainFonts.addAll(fontList);
-        mFallbackFonts.addAll(fallBackList);
-    }
-
-
-    public String getOsFontsLocation() {
-        return mOsFontsLocation;
-    }
-
-    /**
-     * Returns a {@link Font} object given a family name and a style value (constant in
-     * {@link Typeface}).
-     * @param family the family name
-     * @param style a 1-item array containing the requested style. Based on the font being read
-     *              the actual style may be different. The array contains the actual style after
-     *              the method returns.
-     * @return the font object or null if no match could be found.
-     */
-    public synchronized List<Font> getFont(String family, int style) {
-        List<Font> result = new ArrayList<Font>();
-
-        if (family == null) {
-            return result;
-        }
-
-
-        // get the font objects from the main list based on family.
-        for (FontInfo info : mMainFonts) {
-            if (info.families.contains(family)) {
-                result.add(info.font[style]);
-                break;
-            }
-        }
-
-        // add all the fallback fonts for the given style
-        for (FontInfo info : mFallbackFonts) {
-            result.add(info.font[style]);
-        }
-
-        return result;
-    }
-
-
-    public synchronized List<Font> getFallbackFonts(int style) {
-        List<Font> result = new ArrayList<Font>();
-        // add all the fallback fonts
-        for (FontInfo info : mFallbackFonts) {
-            result.add(info.font[style]);
-        }
-        return result;
-    }
-
-
-    private final static class FontInfo {
-        final Font[] font = new Font[4]; // Matches the 4 type-face styles.
-        final Set<String> families;
-
-        FontInfo() {
-            families = new HashSet<String>();
-        }
-    }
-
-    private final static class FontHandler extends DefaultHandler {
-        private final String mOsFontsLocation;
-
-        private FontInfo mFontInfo = null;
-        private final StringBuilder mBuilder = new StringBuilder();
-        private List<FontInfo> mFontList = new ArrayList<FontInfo>();
-        private boolean isCompactFont = true;
-
-        private FontHandler(String osFontsLocation) {
-            super();
-            mOsFontsLocation = osFontsLocation;
-        }
-
-        public List<FontInfo> getFontList() {
-            return mFontList;
-        }
-
-        /* (non-Javadoc)
-         * @see org.xml.sax.helpers.DefaultHandler#startElement(java.lang.String, java.lang.String, java.lang.String, org.xml.sax.Attributes)
-         */
-        @Override
-        public void startElement(String uri, String localName, String name, Attributes attributes)
-                throws SAXException {
-            if (NODE_FAMILYSET.equals(localName)) {
-                mFontList = new ArrayList<FontInfo>();
-            } else if (NODE_FAMILY.equals(localName)) {
-                if (mFontList != null) {
-                    mFontInfo = null;
-                }
-            } else if (NODE_NAME.equals(localName)) {
-                if (mFontList != null && mFontInfo == null) {
-                    mFontInfo = new FontInfo();
-                }
-            } else if (NODE_FILE.equals(localName)) {
-                if (mFontList != null && mFontInfo == null) {
-                    mFontInfo = new FontInfo();
-                }
-                if (ATTRIBUTE_VALUE_ELEGANT.equals(attributes.getValue(ATTRIBUTE_VARIANT))) {
-                    isCompactFont = false;
-                } else {
-                    isCompactFont = true;
-                }
-            }
-
-            mBuilder.setLength(0);
-
-            super.startElement(uri, localName, name, attributes);
-        }
-
-        /* (non-Javadoc)
-         * @see org.xml.sax.helpers.DefaultHandler#characters(char[], int, int)
-         */
-        @Override
-        public void characters(char[] ch, int start, int length) throws SAXException {
-            if (isCompactFont) {
-              mBuilder.append(ch, start, length);
-            }
-        }
-
-        /* (non-Javadoc)
-         * @see org.xml.sax.helpers.DefaultHandler#endElement(java.lang.String, java.lang.String, java.lang.String)
-         */
-        @Override
-        public void endElement(String uri, String localName, String name) throws SAXException {
-            if (NODE_FAMILY.equals(localName)) {
-                if (mFontInfo != null) {
-                    // if has a normal font file, add to the list
-                    if (mFontInfo.font[Typeface.NORMAL] != null) {
-                        mFontList.add(mFontInfo);
-
-                        // create missing font styles, order is important.
-                        if (mFontInfo.font[Typeface.BOLD_ITALIC] == null) {
-                            computeDerivedFont(Typeface.BOLD_ITALIC, DERIVE_BOLD_ITALIC);
-                        }
-                        if (mFontInfo.font[Typeface.ITALIC] == null) {
-                            computeDerivedFont(Typeface.ITALIC, DERIVE_ITALIC);
-                        }
-                        if (mFontInfo.font[Typeface.BOLD] == null) {
-                            computeDerivedFont(Typeface.BOLD, DERIVE_BOLD);
-                        }
-                    }
-
-                    mFontInfo = null;
-                }
-            } else if (NODE_NAME.equals(localName)) {
-                // handle a new name for an existing Font Info
-                if (mFontInfo != null) {
-                    String family = trimXmlWhitespaces(mBuilder.toString());
-                    mFontInfo.families.add(family);
-                }
-            } else if (NODE_FILE.equals(localName)) {
-                // handle a new file for an existing Font Info
-                if (isCompactFont && mFontInfo != null) {
-                    String fileName = trimXmlWhitespaces(mBuilder.toString());
-                    Font font = getFont(fileName);
-                    if (font != null) {
-                        if (fileName.endsWith(FONT_SUFFIX_REGULAR)) {
-                            mFontInfo.font[Typeface.NORMAL] = font;
-                        } else if (fileName.endsWith(FONT_SUFFIX_BOLD)) {
-                            mFontInfo.font[Typeface.BOLD] = font;
-                        } else if (fileName.endsWith(FONT_SUFFIX_BOLDITALIC)) {
-                            mFontInfo.font[Typeface.BOLD_ITALIC] = font;
-                        } else if (fileName.endsWith(FONT_SUFFIX_ITALIC)) {
-                            mFontInfo.font[Typeface.ITALIC] = font;
-                        } else if (fileName.endsWith(FONT_SUFFIX_NONE)) {
-                            mFontInfo.font[Typeface.NORMAL] = font;
-                        }
-                    }
-                }
-            }
-        }
-
-        private Font getFont(String fileName) {
-            try {
-                File file = new File(mOsFontsLocation, fileName);
-                if (file.exists()) {
-                    return Font.createFont(Font.TRUETYPE_FONT, file);
-                }
-            } catch (Exception e) {
-
-            }
-
-            return null;
-        }
-
-        private void computeDerivedFont( int toCompute, int[] basedOnList) {
-            for (int basedOn : basedOnList) {
-                if (mFontInfo.font[basedOn] != null) {
-                    mFontInfo.font[toCompute] =
-                        mFontInfo.font[basedOn].deriveFont(AWT_STYLES[toCompute]);
-                    return;
-                }
-            }
-
-            // we really shouldn't stop there. This means we don't have a NORMAL font...
-            assert false;
-        }
-
-        private String trimXmlWhitespaces(String value) {
-            if (value == null) {
-                return null;
-            }
-
-            // look for carriage return and replace all whitespace around it by just 1 space.
-            int index;
-
-            while ((index = value.indexOf('\n')) != -1) {
-                // look for whitespace on each side
-                int left = index - 1;
-                while (left >= 0) {
-                    if (Character.isWhitespace(value.charAt(left))) {
-                        left--;
-                    } else {
-                        break;
-                    }
-                }
-
-                int right = index + 1;
-                int count = value.length();
-                while (right < count) {
-                    if (Character.isWhitespace(value.charAt(right))) {
-                        right++;
-                    } else {
-                        break;
-                    }
-                }
-
-                // remove all between left and right (non inclusive) and replace by a single space.
-                String leftString = null;
-                if (left >= 0) {
-                    leftString = value.substring(0, left + 1);
-                }
-                String rightString = null;
-                if (right < count) {
-                    rightString = value.substring(right);
-                }
-
-                if (leftString != null) {
-                    value = leftString;
-                    if (rightString != null) {
-                        value += " " + rightString;
-                    }
-                } else {
-                    value = rightString != null ? rightString : "";
-                }
-            }
-
-            // now we un-escape the string
-            int length = value.length();
-            char[] buffer = value.toCharArray();
-
-            for (int i = 0 ; i < length ; i++) {
-                if (buffer[i] == '\\') {
-                    if (buffer[i+1] == 'n') {
-                        // replace the char with \n
-                        buffer[i+1] = '\n';
-                    }
-
-                    // offset the rest of the buffer since we go from 2 to 1 char
-                    System.arraycopy(buffer, i+1, buffer, i, length - i - 1);
-                    length--;
-                }
-            }
-
-            return new String(buffer, 0, length);
-        }
-
-    }
-}
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
index bb72a1e..1f7a28e 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
@@ -125,15 +125,19 @@
         "android.app.Fragment#instantiate", //(Landroid/content/Context;Ljava/lang/String;Landroid/os/Bundle;)Landroid/app/Fragment;",
         "android.content.res.Resources$Theme#obtainStyledAttributes",
         "android.content.res.Resources$Theme#resolveAttribute",
+        "android.content.res.Resources$Theme#resolveAttributes",
         "android.content.res.Resources#localeToLanguageTag",
         "android.content.res.AssetManager#newTheme",
         "android.content.res.AssetManager#deleteTheme",
         "android.content.res.AssetManager#applyThemeStyle",
         "android.content.res.TypedArray#getValueAt",
+        "android.content.res.TypedArray#obtain",
         "android.graphics.BitmapFactory#finishDecode",
+        "android.graphics.Typeface#getSystemFontConfigLocation",
         "android.os.Handler#sendMessageAtTime",
         "android.os.HandlerThread#run",
         "android.text.format.DateFormat#is24HourFormat",
+        "android.util.Xml#newPullParser",
         "android.view.Choreographer#getRefreshRate",
         "android.view.Display#updateDisplayInfoLocked",
         "android.view.Display#getWindowManager",
@@ -170,6 +174,7 @@
         "android.graphics.DiscretePathEffect",
         "android.graphics.DrawFilter",
         "android.graphics.EmbossMaskFilter",
+        "android.graphics.FontFamily",
         "android.graphics.LayerRasterizer",
         "android.graphics.LightingColorFilter",
         "android.graphics.LinearGradient",