Merge "Resolving resources across users."
diff --git a/api/current.txt b/api/current.txt
index f2c88d7..17f5e53 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -567,6 +567,7 @@
     field public static final int freezesText = 16843116; // 0x101016c
     field public static final int fromAlpha = 16843210; // 0x10101ca
     field public static final int fromDegrees = 16843187; // 0x10101b3
+    field public static final int fromId = 16843856; // 0x1010450
     field public static final int fromScene = 16843741; // 0x10103dd
     field public static final int fromXDelta = 16843206; // 0x10101c6
     field public static final int fromXScale = 16843202; // 0x10101c2
@@ -956,6 +957,7 @@
     field public static final int restoreAnyVersion = 16843450; // 0x10102ba
     field public static final deprecated int restoreNeedsApplication = 16843421; // 0x101029d
     field public static final int restrictedAccountType = 16843733; // 0x10103d5
+    field public static final int reversible = 16843857; // 0x1010451
     field public static final int right = 16843183; // 0x10101af
     field public static final int ringtonePreferenceStyle = 16842899; // 0x1010093
     field public static final int ringtoneType = 16843257; // 0x10101f9
@@ -1045,6 +1047,7 @@
     field public static final int spinnerStyle = 16842881; // 0x1010081
     field public static final int spinnersShown = 16843595; // 0x101034b
     field public static final int splitMotionEvents = 16843503; // 0x10102ef
+    field public static final int splitTrack = 16843858; // 0x1010452
     field public static final int src = 16843033; // 0x1010119
     field public static final int ssp = 16843747; // 0x10103e3
     field public static final int sspPattern = 16843749; // 0x10103e5
@@ -1214,6 +1217,7 @@
     field public static final int titleTextStyle = 16843512; // 0x10102f8
     field public static final int toAlpha = 16843211; // 0x10101cb
     field public static final int toDegrees = 16843188; // 0x10101b4
+    field public static final int toId = 16843855; // 0x101044f
     field public static final int toScene = 16843742; // 0x10103de
     field public static final int toXDelta = 16843207; // 0x10101c7
     field public static final int toXScale = 16843203; // 0x10101c3
@@ -3258,6 +3262,7 @@
     method public boolean onContextItemSelected(android.view.MenuItem);
     method public void onContextMenuClosed(android.view.Menu);
     method protected void onCreate(android.os.Bundle);
+    method protected void onCreate(android.os.Bundle, android.os.PersistableBundle);
     method public void onCreateContextMenu(android.view.ContextMenu, android.view.View, android.view.ContextMenu.ContextMenuInfo);
     method public java.lang.CharSequence onCreateDescription();
     method protected deprecated android.app.Dialog onCreateDialog(int);
@@ -3288,6 +3293,7 @@
     method public void onPanelClosed(int, android.view.Menu);
     method protected void onPause();
     method protected void onPostCreate(android.os.Bundle);
+    method protected void onPostCreate(android.os.Bundle, android.os.PersistableBundle);
     method protected void onPostResume();
     method protected deprecated void onPrepareDialog(int, android.app.Dialog);
     method protected deprecated void onPrepareDialog(int, android.app.Dialog, android.os.Bundle);
@@ -3297,9 +3303,11 @@
     method public void onProvideAssistData(android.os.Bundle);
     method protected void onRestart();
     method protected void onRestoreInstanceState(android.os.Bundle);
+    method protected void onRestoreInstanceState(android.os.Bundle, android.os.PersistableBundle);
     method protected void onResume();
     method public deprecated java.lang.Object onRetainNonConfigurationInstance();
     method protected void onSaveInstanceState(android.os.Bundle);
+    method protected void onSaveInstanceState(android.os.Bundle, android.os.PersistableBundle);
     method public boolean onSearchRequested();
     method protected void onStart();
     method protected void onStop();
@@ -4195,14 +4203,18 @@
     method public android.app.Instrumentation.ActivityMonitor addMonitor(android.content.IntentFilter, android.app.Instrumentation.ActivityResult, boolean);
     method public android.app.Instrumentation.ActivityMonitor addMonitor(java.lang.String, android.app.Instrumentation.ActivityResult, boolean);
     method public void callActivityOnCreate(android.app.Activity, android.os.Bundle);
+    method public void callActivityOnCreate(android.app.Activity, android.os.Bundle, android.os.PersistableBundle);
     method public void callActivityOnDestroy(android.app.Activity);
     method public void callActivityOnNewIntent(android.app.Activity, android.content.Intent);
     method public void callActivityOnPause(android.app.Activity);
     method public void callActivityOnPostCreate(android.app.Activity, android.os.Bundle);
+    method public void callActivityOnPostCreate(android.app.Activity, android.os.Bundle, android.os.PersistableBundle);
     method public void callActivityOnRestart(android.app.Activity);
     method public void callActivityOnRestoreInstanceState(android.app.Activity, android.os.Bundle);
+    method public void callActivityOnRestoreInstanceState(android.app.Activity, android.os.Bundle, android.os.PersistableBundle);
     method public void callActivityOnResume(android.app.Activity);
     method public void callActivityOnSaveInstanceState(android.app.Activity, android.os.Bundle);
+    method public void callActivityOnSaveInstanceState(android.app.Activity, android.os.Bundle, android.os.PersistableBundle);
     method public void callActivityOnStart(android.app.Activity);
     method public void callActivityOnStop(android.app.Activity);
     method public void callActivityOnUserLeaving(android.app.Activity);
@@ -4838,6 +4850,7 @@
     method public void clearWindowAnimationFrameStats();
     method public boolean clearWindowContentFrameStats(int);
     method public android.view.accessibility.AccessibilityEvent executeAndWaitForEvent(java.lang.Runnable, android.app.UiAutomation.AccessibilityEventFilter, long) throws java.util.concurrent.TimeoutException;
+    method public android.os.ParcelFileDescriptor executeShellCommand(java.lang.String);
     method public android.view.accessibility.AccessibilityNodeInfo findFocus(int);
     method public android.view.accessibility.AccessibilityNodeInfo getRootInActiveWindow();
     method public final android.accessibilityservice.AccessibilityServiceInfo getServiceInfo();
@@ -7600,7 +7613,7 @@
     method public long getMaxExecutionDelayMillis();
     method public long getMinLatencyMillis();
     method public int getNetworkCapabilities();
-    method public java.lang.String getServiceClassName();
+    method public android.content.ComponentName getService();
     method public int getTaskId();
     method public boolean isPeriodic();
     method public boolean isRequireCharging();
@@ -7615,7 +7628,7 @@
   }
 
   public final class Task.Builder {
-    ctor public Task.Builder(int, java.lang.Class<android.app.task.TaskService>);
+    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);
@@ -10822,6 +10835,12 @@
     method public abstract void stop();
   }
 
+  public class AnimatedStateListDrawable extends android.graphics.drawable.StateListDrawable {
+    ctor public AnimatedStateListDrawable();
+    method public void addState(int[], android.graphics.drawable.Drawable, int);
+    method public void addTransition(int, int, android.graphics.drawable.AnimationDrawable, boolean);
+  }
+
   public class AnimationDrawable extends android.graphics.drawable.DrawableContainer implements android.graphics.drawable.Animatable java.lang.Runnable {
     ctor public AnimationDrawable();
     method public void addFrame(android.graphics.drawable.Drawable, int);
@@ -13322,6 +13341,45 @@
     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
@@ -13366,6 +13424,7 @@
     field public static final int ENCODING_INVALID = 0; // 0x0
     field public static final int ENCODING_PCM_16BIT = 2; // 0x2
     field public static final int ENCODING_PCM_8BIT = 3; // 0x3
+    field public static final int ENCODING_PCM_FLOAT = 4; // 0x4
   }
 
   public class AudioManager {
@@ -13583,6 +13642,7 @@
     method public void stop() throws java.lang.IllegalStateException;
     method public int write(byte[], int, int);
     method public int write(short[], int, int);
+    method public int write(float[], int, int, int);
     method public int write(java.nio.ByteBuffer, int, int);
     field public static final int ERROR = -1; // 0xffffffff
     field public static final int ERROR_BAD_VALUE = -2; // 0xfffffffe
@@ -13815,6 +13875,7 @@
     method public final void queueSecureInputBuffer(int, int, android.media.MediaCodec.CryptoInfo, long, int) throws android.media.MediaCodec.CryptoException;
     method public final void release();
     method public final void releaseOutputBuffer(int, boolean);
+    method public final void releaseOutputBuffer(int, long);
     method public void setNotificationCallback(android.media.MediaCodec.NotificationCallback);
     method public final void setParameters(android.os.Bundle);
     method public final void setVideoScalingMode(int);
@@ -23437,6 +23498,7 @@
     method public static boolean putLong(android.content.ContentResolver, java.lang.String, long);
     method public static boolean putString(android.content.ContentResolver, java.lang.String, java.lang.String);
     method public static final deprecated void setLocationProviderEnabled(android.content.ContentResolver, java.lang.String, boolean);
+    field public static final java.lang.String ACCESSIBILITY_DISPLAY_INVERSION_ENABLED = "accessibility_display_inversion_enabled";
     field public static final java.lang.String ACCESSIBILITY_ENABLED = "accessibility_enabled";
     field public static final java.lang.String ACCESSIBILITY_SPEAK_PASSWORD = "speak_password";
     field public static final deprecated java.lang.String ADB_ENABLED = "adb_enabled";
@@ -25161,16 +25223,24 @@
     method public final deprecated void cancelNotification(java.lang.String, java.lang.String, int);
     method public final void cancelNotification(java.lang.String);
     method public final void cancelNotifications(java.lang.String[]);
-    method public java.lang.String[] getActiveNotificationKeys();
     method public android.service.notification.StatusBarNotification[] getActiveNotifications();
     method public android.service.notification.StatusBarNotification[] getActiveNotifications(java.lang.String[]);
+    method public java.lang.String[] getOrderedNotificationKeys();
     method public android.os.IBinder onBind(android.content.Intent);
     method public void onListenerConnected(java.lang.String[]);
+    method public void onNotificationOrderUpdate();
     method public abstract void onNotificationPosted(android.service.notification.StatusBarNotification);
     method public abstract void onNotificationRemoved(android.service.notification.StatusBarNotification);
     field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.NotificationListenerService";
   }
 
+  public class NotificationOrderUpdate implements android.os.Parcelable {
+    ctor public NotificationOrderUpdate(android.os.Parcel);
+    method public int describeContents();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator CREATOR;
+  }
+
   public class StatusBarNotification implements android.os.Parcelable {
     ctor public StatusBarNotification(java.lang.String, java.lang.String, int, java.lang.String, int, int, int, android.app.Notification, android.os.UserHandle, long);
     ctor public StatusBarNotification(android.os.Parcel);
@@ -25768,6 +25838,7 @@
     method public static boolean isatty(java.io.FileDescriptor);
     method public static void kill(int, int) throws android.system.ErrnoException;
     method public static void lchown(java.lang.String, int, int) throws android.system.ErrnoException;
+    method public static void link(java.lang.String, java.lang.String) throws android.system.ErrnoException;
     method public static void listen(java.io.FileDescriptor, int) throws android.system.ErrnoException;
     method public static long lseek(java.io.FileDescriptor, long, int) throws android.system.ErrnoException;
     method public static android.system.StructStat lstat(java.lang.String) throws android.system.ErrnoException;
@@ -29108,11 +29179,11 @@
   }
 
   public final class TvInputManager {
-    method public void createSession(android.content.ComponentName, android.tv.TvInputManager.SessionCreateCallback, android.os.Handler);
-    method public boolean getAvailability(android.content.ComponentName);
+    method public void createSession(java.lang.String, android.tv.TvInputManager.SessionCallback, android.os.Handler);
+    method public boolean getAvailability(java.lang.String);
     method public java.util.List<android.tv.TvInputInfo> getTvInputList();
-    method public void registerListener(android.content.ComponentName, android.tv.TvInputManager.TvInputListener, android.os.Handler);
-    method public void unregisterListener(android.content.ComponentName, android.tv.TvInputManager.TvInputListener);
+    method public void registerListener(java.lang.String, android.tv.TvInputManager.TvInputListener, android.os.Handler);
+    method public void unregisterListener(java.lang.String, android.tv.TvInputManager.TvInputListener);
   }
 
   public static final class TvInputManager.Session {
@@ -29121,13 +29192,15 @@
     method public void tune(android.net.Uri);
   }
 
-  public static abstract interface TvInputManager.SessionCreateCallback {
-    method public abstract void onSessionCreated(android.tv.TvInputManager.Session);
+  public static abstract class TvInputManager.SessionCallback {
+    ctor public TvInputManager.SessionCallback();
+    method public void onSessionCreated(android.tv.TvInputManager.Session);
+    method public void onSessionReleased(android.tv.TvInputManager.Session);
   }
 
   public static abstract class TvInputManager.TvInputListener {
     ctor public TvInputManager.TvInputListener();
-    method public void onAvailabilityChanged(android.content.ComponentName, boolean);
+    method public void onAvailabilityChanged(java.lang.String, boolean);
   }
 
   public abstract class TvInputService extends android.app.Service {
@@ -29159,7 +29232,7 @@
     ctor public TvView(android.content.Context);
     ctor public TvView(android.content.Context, android.util.AttributeSet);
     ctor public TvView(android.content.Context, android.util.AttributeSet, int);
-    method public void bindTvInput(android.content.ComponentName, android.tv.TvInputManager.SessionCreateCallback);
+    method public void bindTvInput(java.lang.String, android.tv.TvInputManager.SessionCallback);
     method public boolean dispatchUnhandledInputEvent(android.view.InputEvent);
     method public boolean onUnhandledInputEvent(android.view.InputEvent);
     method public void setOnUnhandledInputEventListener(android.tv.TvView.OnUnhandledInputEventListener);
@@ -33735,7 +33808,6 @@
     method public abstract long getResources();
     method public abstract void grant(long);
     field public static final long RESOURCE_AUDIO_CAPTURE = 4L; // 0x4L
-    field public static final long RESOURCE_GEOLOCATION = 1L; // 0x1L
     field public static final long RESOURCE_VIDEO_CAPTURE = 2L; // 0x2L
   }
 
@@ -34326,9 +34398,11 @@
     ctor public AbsSeekBar(android.content.Context, android.util.AttributeSet, int);
     ctor public AbsSeekBar(android.content.Context, android.util.AttributeSet, int, int);
     method public int getKeyProgressIncrement();
+    method public boolean getSplitTrack();
     method public android.graphics.drawable.Drawable getThumb();
     method public int getThumbOffset();
     method public void setKeyProgressIncrement(int);
+    method public void setSplitTrack(boolean);
     method public void setThumb(android.graphics.drawable.Drawable);
     method public void setThumbOffset(int);
   }
@@ -34865,9 +34939,11 @@
     ctor public EdgeEffect(android.content.Context);
     method public boolean draw(android.graphics.Canvas);
     method public void finish();
+    method public int getMaxHeight();
     method public boolean isFinished();
     method public void onAbsorb(int);
     method public void onPull(float);
+    method public void onPull(float, float);
     method public void onRelease();
     method public void setSize(int, int);
   }
@@ -36131,6 +36207,7 @@
     ctor public Switch(android.content.Context, android.util.AttributeSet);
     ctor public Switch(android.content.Context, android.util.AttributeSet, int);
     ctor public Switch(android.content.Context, android.util.AttributeSet, int, int);
+    method public boolean getSplitTrack();
     method public int getSwitchMinWidth();
     method public int getSwitchPadding();
     method public java.lang.CharSequence getTextOff();
@@ -36139,6 +36216,7 @@
     method public int getThumbTextPadding();
     method public android.graphics.drawable.Drawable getTrackDrawable();
     method public void onMeasure(int, int);
+    method public void setSplitTrack(boolean);
     method public void setSwitchMinWidth(int);
     method public void setSwitchPadding(int);
     method public void setSwitchTextAppearance(android.content.Context, int);
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index ef6fcb7..b4b3c99 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -17,6 +17,7 @@
 package android.app;
 
 import android.annotation.NonNull;
+import android.os.PersistableBundle;
 import android.transition.Scene;
 import android.transition.TransitionManager;
 import android.util.ArrayMap;
@@ -922,6 +923,30 @@
     }
 
     /**
+     * Same as {@link #onCreate(android.os.Bundle)} but called for those activities created with
+     * the attribute {@link android.R.attr#persistable} set true.
+     *
+     * @param savedInstanceState if the activity is being re-initialized after
+     *     previously being shut down then this Bundle contains the data it most
+     *     recently supplied in {@link #onSaveInstanceState}.
+     *     <b><i>Note: Otherwise it is null.</i></b>
+     * @param persistentState if the activity is being re-initialized after
+     *     previously being shut down or powered off then this Bundle contains the data it most
+     *     recently supplied to outPersistentState in {@link #onSaveInstanceState}.
+     *     <b><i>Note: Otherwise it is null.</i></b>
+     *
+     * @see #onCreate(android.os.Bundle)
+     * @see #onStart
+     * @see #onSaveInstanceState
+     * @see #onRestoreInstanceState
+     * @see #onPostCreate
+     */
+    protected void onCreate(@Nullable Bundle savedInstanceState,
+            @Nullable PersistableBundle persistentState) {
+        onCreate(savedInstanceState);
+    }
+
+    /**
      * The hook for {@link ActivityThread} to restore the state of this activity.
      *
      * Calls {@link #onSaveInstanceState(android.os.Bundle)} and
@@ -935,6 +960,23 @@
     }
 
     /**
+     * The hook for {@link ActivityThread} to restore the state of this activity.
+     *
+     * Calls {@link #onSaveInstanceState(android.os.Bundle)} and
+     * {@link #restoreManagedDialogs(android.os.Bundle)}.
+     *
+     * @param savedInstanceState contains the saved state
+     * @param persistentState contains the persistable saved state
+     */
+    final void performRestoreInstanceState(Bundle savedInstanceState,
+            PersistableBundle persistentState) {
+        onRestoreInstanceState(savedInstanceState, persistentState);
+        if (savedInstanceState != null) {
+            restoreManagedDialogs(savedInstanceState);
+        }
+    }
+
+    /**
      * This method is called after {@link #onStart} when the activity is
      * being re-initialized from a previously saved state, given here in
      * <var>savedInstanceState</var>.  Most implementations will simply use {@link #onCreate}
@@ -962,7 +1004,34 @@
             }
         }
     }
-    
+
+    /**
+     * This is the same as {@link #onRestoreInstanceState(Bundle)} but is called for activities
+     * created with the attribute {@link android.R.attr#persistable}. The {@link
+     * android.os.PersistableBundle} passed came from the restored PersistableBundle first
+     * saved in {@link #onSaveInstanceState(Bundle, PersistableBundle)}.
+     *
+     * <p>This method is called between {@link #onStart} and
+     * {@link #onPostCreate}.
+     *
+     * <p>If this method is called {@link #onRestoreInstanceState(Bundle)} will not be called.
+     *
+     * @param savedInstanceState the data most recently supplied in {@link #onSaveInstanceState}.
+     * @param persistentState the data most recently supplied in {@link #onSaveInstanceState}.
+     *
+     * @see #onRestoreInstanceState(Bundle)
+     * @see #onCreate
+     * @see #onPostCreate
+     * @see #onResume
+     * @see #onSaveInstanceState
+     */
+    protected void onRestoreInstanceState(Bundle savedInstanceState,
+            PersistableBundle persistentState) {
+        if (savedInstanceState != null) {
+            onRestoreInstanceState(savedInstanceState);
+        }
+    }
+
     /**
      * Restore the state of any saved managed dialogs.
      *
@@ -1039,6 +1108,21 @@
     }
 
     /**
+     * This is the same as {@link #onPostCreate(Bundle)} but is called for activities
+     * created with the attribute {@link android.R.attr#persistable}.
+     *
+     * @param savedInstanceState The data most recently supplied in {@link #onSaveInstanceState}
+     * @param persistentState The data caming from the PersistableBundle first
+     * saved in {@link #onSaveInstanceState(Bundle, PersistableBundle)}.
+     *
+     * @see #onCreate
+     */
+    protected void onPostCreate(@Nullable Bundle savedInstanceState,
+            @Nullable PersistableBundle persistentState) {
+        onPostCreate(savedInstanceState);
+    }
+
+    /**
      * Called after {@link #onCreate} &mdash; or after {@link #onRestart} when  
      * the activity had been stopped, but is now again being displayed to the 
      * user.  It will be followed by {@link #onResume}.
@@ -1194,6 +1278,22 @@
     }
 
     /**
+     * The hook for {@link ActivityThread} to save the state of this activity.
+     *
+     * Calls {@link #onSaveInstanceState(android.os.Bundle)}
+     * and {@link #saveManagedDialogs(android.os.Bundle)}.
+     *
+     * @param outState The bundle to save the state to.
+     * @param outPersistentState The bundle to save persistent state to.
+     */
+    final void performSaveInstanceState(Bundle outState, PersistableBundle outPersistentState) {
+        onSaveInstanceState(outState, outPersistentState);
+        saveManagedDialogs(outState);
+        if (DEBUG_LIFECYCLE) Slog.v(TAG, "onSaveInstanceState " + this + ": " + outState +
+                ", " + outPersistentState);
+    }
+
+    /**
      * Called to retrieve per-instance state from an activity before being killed
      * so that the state can be restored in {@link #onCreate} or
      * {@link #onRestoreInstanceState} (the {@link Bundle} populated by this method
@@ -1248,6 +1348,25 @@
     }
 
     /**
+     * This is the same as {@link #onSaveInstanceState} but is called for activities
+     * created with the attribute {@link android.R.attr#persistable}. The {@link
+     * android.os.PersistableBundle} passed in will be saved and presented in
+     * {@link #onCreate(Bundle, PersistableBundle)} the first time that this activity
+     * is restarted following the next device reboot.
+     *
+     * @param outState Bundle in which to place your saved state.
+     * @param outPersistentState State which will be saved across reboots.
+     *
+     * @see #onSaveInstanceState(Bundle)
+     * @see #onCreate
+     * @see #onRestoreInstanceState(Bundle, PersistableBundle)
+     * @see #onPause
+     */
+    protected void onSaveInstanceState(Bundle outState, PersistableBundle outPersistentState) {
+        onSaveInstanceState(outState);
+    }
+
+    /**
      * Save the state of any managed dialogs.
      *
      * @param outState place to store the saved state.
@@ -3499,13 +3618,15 @@
         }
 
         // Get the primary color and update the RecentsActivityValues for this activity
-        TypedArray a = getTheme().obtainStyledAttributes(com.android.internal.R.styleable.Theme);
-        int colorPrimary = a.getColor(com.android.internal.R.styleable.Theme_colorPrimary, 0);
-        a.recycle();
-        if (colorPrimary != 0) {
-            ActivityManager.RecentsActivityValues v = new ActivityManager.RecentsActivityValues();
-            v.colorPrimary = colorPrimary;
-            setRecentsActivityValues(v);
+        if (theme != null) {
+            TypedArray a = theme.obtainStyledAttributes(com.android.internal.R.styleable.Theme);
+            int colorPrimary = a.getColor(com.android.internal.R.styleable.Theme_colorPrimary, 0);
+            a.recycle();
+            if (colorPrimary != 0) {
+                ActivityManager.RecentsActivityValues v = new ActivityManager.RecentsActivityValues();
+                v.colorPrimary = colorPrimary;
+                setRecentsActivityValues(v);
+            }
         }
     }
 
@@ -5489,13 +5610,22 @@
         return mParent != null ? mParent.getActivityToken() : mToken;
     }
 
-    final void performCreate(Bundle icicle) {
-        onCreate(icicle);
+    final void performCreateCommon() {
         mVisibleFromClient = !mWindow.getWindowStyle().getBoolean(
                 com.android.internal.R.styleable.Window_windowNoDisplay, false);
         mFragments.dispatchActivityCreated();
     }
 
+    final void performCreate(Bundle icicle) {
+        onCreate(icicle);
+        performCreateCommon();
+    }
+
+    final void performCreate(Bundle icicle, PersistableBundle persistentState) {
+        onCreate(icicle, persistentState);
+        performCreateCommon();
+    }
+
     final void performStart() {
         mFragments.noteStateNotSaved();
         mCalled = false;
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 263a675..296e9d3 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -40,6 +40,7 @@
 import android.os.Parcel;
 import android.os.ParcelFileDescriptor;
 import android.os.Parcelable;
+import android.os.PersistableBundle;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.StrictMode;
@@ -454,7 +455,8 @@
         case ACTIVITY_PAUSED_TRANSACTION: {
             data.enforceInterface(IActivityManager.descriptor);
             IBinder token = data.readStrongBinder();
-            activityPaused(token);
+            PersistableBundle persistentState = data.readPersistableBundle();
+            activityPaused(token, persistentState);
             reply.writeNoException();
             return true;
         }
@@ -463,10 +465,9 @@
             data.enforceInterface(IActivityManager.descriptor);
             IBinder token = data.readStrongBinder();
             Bundle map = data.readBundle();
-            Bitmap thumbnail = data.readInt() != 0
-                ? Bitmap.CREATOR.createFromParcel(data) : null;
+            PersistableBundle persistentState = data.readPersistableBundle();
             CharSequence description = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(data);
-            activityStopped(token, map, thumbnail, description);
+            activityStopped(token, map, persistentState, description);
             reply.writeNoException();
             return true;
         }
@@ -2591,31 +2592,27 @@
         data.recycle();
         reply.recycle();
     }
-    public void activityPaused(IBinder token) throws RemoteException
+    public void activityPaused(IBinder token, PersistableBundle persistentState) throws RemoteException
     {
         Parcel data = Parcel.obtain();
         Parcel reply = Parcel.obtain();
         data.writeInterfaceToken(IActivityManager.descriptor);
         data.writeStrongBinder(token);
+        data.writePersistableBundle(persistentState);
         mRemote.transact(ACTIVITY_PAUSED_TRANSACTION, data, reply, 0);
         reply.readException();
         data.recycle();
         reply.recycle();
     }
     public void activityStopped(IBinder token, Bundle state,
-            Bitmap thumbnail, CharSequence description) throws RemoteException
+            PersistableBundle persistentState, CharSequence description) throws RemoteException
     {
         Parcel data = Parcel.obtain();
         Parcel reply = Parcel.obtain();
         data.writeInterfaceToken(IActivityManager.descriptor);
         data.writeStrongBinder(token);
         data.writeBundle(state);
-        if (thumbnail != null) {
-            data.writeInt(1);
-            thumbnail.writeToParcel(data, 0);
-        } else {
-            data.writeInt(0);
-        }
+        data.writePersistableBundle(persistentState);
         TextUtils.writeToParcel(description, data, 0);
         mRemote.transact(ACTIVITY_STOPPED_TRANSACTION, data, reply, IBinder.FLAG_ONEWAY);
         reply.readException();
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index b606088..4cf30ae 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -47,6 +47,7 @@
 import android.net.IConnectivityManager;
 import android.net.Proxy;
 import android.net.ProxyInfo;
+import android.net.Uri;
 import android.opengl.GLUtils;
 import android.os.AsyncTask;
 import android.os.Binder;
@@ -56,11 +57,11 @@
 import android.os.Environment;
 import android.os.Handler;
 import android.os.IBinder;
-import android.os.IRemoteCallback;
 import android.os.Looper;
 import android.os.Message;
 import android.os.MessageQueue;
 import android.os.ParcelFileDescriptor;
+import android.os.PersistableBundle;
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.ServiceManager;
@@ -69,8 +70,6 @@
 import android.os.SystemProperties;
 import android.os.Trace;
 import android.os.UserHandle;
-import android.transition.Scene;
-import android.transition.TransitionManager;
 import android.provider.Settings;
 import android.util.AndroidRuntimeException;
 import android.util.ArrayMap;
@@ -268,6 +267,7 @@
         Intent intent;
         IVoiceInteractor voiceInteractor;
         Bundle state;
+        PersistableBundle persistentState;
         Activity activity;
         Window window;
         Activity parent;
@@ -317,6 +317,10 @@
             return false;
         }
 
+        public boolean isPersistable() {
+            return (activityInfo.flags & ActivityInfo.FLAG_PERSISTABLE) != 0;
+        }
+
         public String toString() {
             ComponentName componentName = intent != null ? intent.getComponent() : null;
             return "ActivityRecord{"
@@ -605,8 +609,8 @@
         // activity itself back to the activity manager. (matters more with ipc)
         public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
                 ActivityInfo info, Configuration curConfig, CompatibilityInfo compatInfo,
-                IVoiceInteractor voiceInteractor,
-                int procState, Bundle state, List<ResultInfo> pendingResults,
+                IVoiceInteractor voiceInteractor, int procState, Bundle state,
+                PersistableBundle persistentState, List<ResultInfo> pendingResults,
                 List<Intent> pendingNewIntents, boolean notResumed, boolean isForward,
                 String profileName, ParcelFileDescriptor profileFd, boolean autoStopProfiler,
                 Bundle resumeArgs) {
@@ -622,6 +626,7 @@
             r.activityInfo = info;
             r.compatInfo = compatInfo;
             r.state = state;
+            r.persistentState = persistentState;
 
             r.pendingResults = pendingResults;
             r.pendingIntents = pendingNewIntents;
@@ -835,7 +840,7 @@
             InetAddress.clearDnsCache();
         }
 
-        public void setHttpProxy(String host, String port, String exclList, String pacFileUrl) {
+        public void setHttpProxy(String host, String port, String exclList, Uri pacFileUrl) {
             Proxy.setHttpProxySystemProperty(host, port, exclList, pacFileUrl);
         }
 
@@ -2205,7 +2210,11 @@
                 }
 
                 activity.mCalled = false;
-                mInstrumentation.callActivityOnCreate(activity, r.state);
+                if (r.isPersistable()) {
+                    mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
+                } else {
+                    mInstrumentation.callActivityOnCreate(activity, r.state);
+                }
                 if (!activity.mCalled) {
                     throw new SuperNotCalledException(
                         "Activity " + r.intent.getComponent().toShortString() +
@@ -2218,13 +2227,23 @@
                     r.stopped = false;
                 }
                 if (!r.activity.mFinished) {
-                    if (r.state != null) {
+                    if (r.isPersistable()) {
+                        if (r.state != null || r.persistentState != null) {
+                            mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state,
+                                    r.persistentState);
+                        }
+                    } else if (r.state != null) {
                         mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state);
                     }
                 }
                 if (!r.activity.mFinished) {
                     activity.mCalled = false;
-                    mInstrumentation.callActivityOnPostCreate(activity, r.state);
+                    if (r.isPersistable()) {
+                        mInstrumentation.callActivityOnPostCreate(activity, r.state,
+                                r.persistentState);
+                    } else {
+                        mInstrumentation.callActivityOnPostCreate(activity, r.state);
+                    }
                     if (!activity.mCalled) {
                         throw new SuperNotCalledException(
                             "Activity " + r.intent.getComponent().toShortString() +
@@ -2842,6 +2861,7 @@
                 r.paused = false;
                 r.stopped = false;
                 r.state = null;
+                r.persistentState = null;
             } catch (Exception e) {
                 if (!mInstrumentation.onException(r.activity, e)) {
                     throw new RuntimeException(
@@ -3069,7 +3089,7 @@
 
             // Tell the activity manager we have paused.
             try {
-                ActivityManagerNative.getDefault().activityPaused(token);
+                ActivityManagerNative.getDefault().activityPaused(token, r.persistentState);
             } catch (RemoteException ex) {
             }
         }
@@ -3099,17 +3119,13 @@
                     + r.intent.getComponent().toShortString());
             Slog.e(TAG, e.getMessage(), e);
         }
-        Bundle state = null;
         if (finished) {
             r.activity.mFinished = true;
         }
         try {
             // Next have the activity save its current state and managed dialogs...
             if (!r.activity.mFinished && saveState) {
-                state = new Bundle();
-                state.setAllowFds(false);
-                mInstrumentation.callActivityOnSaveInstanceState(r.activity, state);
-                r.state = state;
+                callCallActivityOnSaveInstanceState(r);
             }
             // Now we are idle.
             r.activity.mCalled = false;
@@ -3145,7 +3161,7 @@
             listeners.get(i).onPaused(r.activity);
         }
 
-        return state;
+        return !r.activity.mFinished && saveState ? r.state : null;
     }
 
     final void performStopActivity(IBinder token, boolean saveState) {
@@ -3156,7 +3172,7 @@
     private static class StopInfo implements Runnable {
         ActivityClientRecord activity;
         Bundle state;
-        Bitmap thumbnail;
+        PersistableBundle persistentState;
         CharSequence description;
 
         @Override public void run() {
@@ -3164,7 +3180,7 @@
             try {
                 if (DEBUG_MEMORY_TRIM) Slog.v(TAG, "Reporting activity stopped: " + activity);
                 ActivityManagerNative.getDefault().activityStopped(
-                    activity.token, state, thumbnail, description);
+                    activity.token, state, persistentState, description);
             } catch (RemoteException ex) {
             }
         }
@@ -3203,7 +3219,6 @@
     private void performStopActivityInner(ActivityClientRecord r,
             StopInfo info, boolean keepShown, boolean saveState) {
         if (localLOGV) Slog.v(TAG, "Performing stop of " + r);
-        Bundle state = null;
         if (r != null) {
             if (!keepShown && r.stopped) {
                 if (r.activity.mFinished) {
@@ -3223,7 +3238,6 @@
                     // First create a thumbnail for the activity...
                     // For now, don't create the thumbnail here; we are
                     // doing that by doing a screen snapshot.
-                    info.thumbnail = null; //createThumbnailBitmap(r);
                     info.description = r.activity.onCreateDescription();
                 } catch (Exception e) {
                     if (!mInstrumentation.onException(r.activity, e)) {
@@ -3238,12 +3252,7 @@
             // Next have the activity save its current state and managed dialogs...
             if (!r.activity.mFinished && saveState) {
                 if (r.state == null) {
-                    state = new Bundle();
-                    state.setAllowFds(false);
-                    mInstrumentation.callActivityOnSaveInstanceState(r.activity, state);
-                    r.state = state;
-                } else {
-                    state = r.state;
+                    callCallActivityOnSaveInstanceState(r);
                 }
             }
 
@@ -3319,6 +3328,7 @@
         // manager to proceed and allow us to go fully into the background.
         info.activity = r;
         info.state = r.state;
+        info.persistentState = r.persistentState;
         mH.post(info);
     }
 
@@ -3775,9 +3785,7 @@
             performPauseActivity(r.token, false, r.isPreHoneycomb());
         }
         if (r.state == null && !r.stopped && !r.isPreHoneycomb()) {
-            r.state = new Bundle();
-            r.state.setAllowFds(false);
-            mInstrumentation.callActivityOnSaveInstanceState(r.activity, r.state);
+            callCallActivityOnSaveInstanceState(r);
         }
 
         handleDestroyActivity(r.token, false, configChanges, true);
@@ -3807,6 +3815,18 @@
         handleLaunchActivity(r, currentIntent);
     }
 
+    private void callCallActivityOnSaveInstanceState(ActivityClientRecord r) {
+        r.state = new Bundle();
+        r.state.setAllowFds(false);
+        if (r.isPersistable()) {
+            r.persistentState = new PersistableBundle();
+            mInstrumentation.callActivityOnSaveInstanceState(r.activity, r.state,
+                    r.persistentState);
+        } else {
+            mInstrumentation.callActivityOnSaveInstanceState(r.activity, r.state);
+        }
+    }
+
     ArrayList<ComponentCallbacks2> collectComponentCallbacks(
             boolean allActivities, Configuration newConfig) {
         ArrayList<ComponentCallbacks2> callbacks
diff --git a/core/java/android/app/ApplicationThreadNative.java b/core/java/android/app/ApplicationThreadNative.java
index 7f2fb59..0029efa 100644
--- a/core/java/android/app/ApplicationThreadNative.java
+++ b/core/java/android/app/ApplicationThreadNative.java
@@ -25,10 +25,12 @@
 import android.content.pm.ServiceInfo;
 import android.content.res.CompatibilityInfo;
 import android.content.res.Configuration;
+import android.net.Uri;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.Debug;
 import android.os.Parcelable;
+import android.os.PersistableBundle;
 import android.os.RemoteException;
 import android.os.IBinder;
 import android.os.Parcel;
@@ -141,6 +143,7 @@
                     data.readStrongBinder());
             int procState = data.readInt();
             Bundle state = data.readBundle();
+            PersistableBundle persistentState = data.readPersistableBundle();
             List<ResultInfo> ri = data.createTypedArrayList(ResultInfo.CREATOR);
             List<Intent> pi = data.createTypedArrayList(Intent.CREATOR);
             boolean notResumed = data.readInt() != 0;
@@ -151,9 +154,9 @@
             boolean autoStopProfiler = data.readInt() != 0;
             Bundle resumeArgs = data.readBundle();
             scheduleLaunchActivity(intent, b, ident, info, curConfig, compatInfo,
-                    voiceInteractor, procState, state,
-                    ri, pi, notResumed, isForward, profileName, profileFd, autoStopProfiler,
-                    resumeArgs);
+                    voiceInteractor, procState, state, persistentState,
+                    ri, pi, notResumed, isForward, profileName, profileFd,
+                    autoStopProfiler, resumeArgs);
             return true;
         }
         
@@ -337,7 +340,7 @@
             final String proxy = data.readString();
             final String port = data.readString();
             final String exclList = data.readString();
-            final String pacFileUrl = data.readString();
+            final Uri pacFileUrl = Uri.CREATOR.createFromParcel(data);
             setHttpProxy(proxy, port, exclList, pacFileUrl);
             return true;
         }
@@ -731,8 +734,8 @@
 
     public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
             ActivityInfo info, Configuration curConfig, CompatibilityInfo compatInfo,
-            IVoiceInteractor voiceInteractor,
-            int procState, Bundle state, List<ResultInfo> pendingResults,
+            IVoiceInteractor voiceInteractor, int procState, Bundle state,
+            PersistableBundle persistentState, List<ResultInfo> pendingResults,
             List<Intent> pendingNewIntents, boolean notResumed, boolean isForward,
             String profileName, ParcelFileDescriptor profileFd, boolean autoStopProfiler,
             Bundle resumeArgs)
@@ -748,6 +751,7 @@
         data.writeStrongBinder(voiceInteractor != null ? voiceInteractor.asBinder() : null);
         data.writeInt(procState);
         data.writeBundle(state);
+        data.writePersistableBundle(persistentState);
         data.writeTypedList(pendingResults);
         data.writeTypedList(pendingNewIntents);
         data.writeInt(notResumed ? 1 : 0);
@@ -1005,13 +1009,13 @@
     }
 
     public void setHttpProxy(String proxy, String port, String exclList,
-            String pacFileUrl) throws RemoteException {
+            Uri pacFileUrl) throws RemoteException {
         Parcel data = Parcel.obtain();
         data.writeInterfaceToken(IApplicationThread.descriptor);
         data.writeString(proxy);
         data.writeString(port);
         data.writeString(exclList);
-        data.writeString(pacFileUrl);
+        pacFileUrl.writeToParcel(data, 0);
         mRemote.transact(SET_HTTP_PROXY_TRANSACTION, data, null, IBinder.FLAG_ONEWAY);
         data.recycle();
     }
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 730c3cb..a30a64a 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -45,6 +45,7 @@
 import android.os.Parcel;
 import android.os.ParcelFileDescriptor;
 import android.os.Parcelable;
+import android.os.PersistableBundle;
 import android.os.RemoteException;
 import android.os.StrictMode;
 import android.service.voice.IVoiceInteractionSession;
@@ -104,9 +105,9 @@
     public void activityResumed(IBinder token) throws RemoteException;
     public void activityIdle(IBinder token, Configuration config,
             boolean stopProfiling) throws RemoteException;
-    public void activityPaused(IBinder token) throws RemoteException;
+    public void activityPaused(IBinder token, PersistableBundle persistentState) throws RemoteException;
     public void activityStopped(IBinder token, Bundle state,
-            Bitmap thumbnail, CharSequence description) throws RemoteException;
+            PersistableBundle persistentState, CharSequence description) throws RemoteException;
     public void activitySlept(IBinder token) throws RemoteException;
     public void activityDestroyed(IBinder token) throws RemoteException;
     public String getCallingPackage(IBinder token) throws RemoteException;
diff --git a/core/java/android/app/IApplicationThread.java b/core/java/android/app/IApplicationThread.java
index fefba8a..d5fbd0b 100644
--- a/core/java/android/app/IApplicationThread.java
+++ b/core/java/android/app/IApplicationThread.java
@@ -25,9 +25,11 @@
 import android.content.pm.ServiceInfo;
 import android.content.res.CompatibilityInfo;
 import android.content.res.Configuration;
+import android.net.Uri;
 import android.os.Bundle;
 import android.os.Debug;
 import android.os.ParcelFileDescriptor;
+import android.os.PersistableBundle;
 import android.os.RemoteException;
 import android.os.IBinder;
 import android.os.IInterface;
@@ -58,8 +60,8 @@
     void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
             ActivityInfo info, Configuration curConfig, CompatibilityInfo compatInfo,
             IVoiceInteractor voiceInteractor, int procState, Bundle state,
-            List<ResultInfo> pendingResults, List<Intent> pendingNewIntents, boolean notResumed,
-            boolean isForward,
+            PersistableBundle persistentState, List<ResultInfo> pendingResults,
+            List<Intent> pendingNewIntents, boolean notResumed, boolean isForward,
             String profileName, ParcelFileDescriptor profileFd, boolean autoStopProfiler,
             Bundle resumeArgs)
             throws RemoteException;
@@ -105,7 +107,7 @@
     void updateTimeZone() throws RemoteException;
     void clearDnsCache() throws RemoteException;
     void setHttpProxy(String proxy, String port, String exclList,
-            String pacFileUrl) throws RemoteException;
+            Uri pacFileUrl) throws RemoteException;
     void processInBackground() throws RemoteException;
     void dumpService(FileDescriptor fd, IBinder servicetoken, String[] args)
             throws RemoteException;
diff --git a/core/java/android/app/IUiAutomationConnection.aidl b/core/java/android/app/IUiAutomationConnection.aidl
index 347de97..8ab9ac3 100644
--- a/core/java/android/app/IUiAutomationConnection.aidl
+++ b/core/java/android/app/IUiAutomationConnection.aidl
@@ -43,4 +43,5 @@
     WindowContentFrameStats getWindowContentFrameStats(int windowId);
     void clearWindowAnimationFrameStats();
     WindowAnimationFrameStats getWindowAnimationFrameStats();
+    void executeShellCommand(String command, in ParcelFileDescriptor fd);
 }
diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java
index e58ccb8..bb3e002 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -30,6 +30,7 @@
 import android.os.Looper;
 import android.os.MessageQueue;
 import android.os.PerformanceCollector;
+import android.os.PersistableBundle;
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.ServiceManager;
@@ -1061,15 +1062,7 @@
         return (Activity)cl.loadClass(className).newInstance();
     }
 
-    /**
-     * Perform calling of an activity's {@link Activity#onCreate}
-     * method.  The default implementation simply calls through to that method.
-     * 
-     * @param activity The activity being created.
-     * @param icicle The previously frozen state (or null) to pass through to
-     *               onCreate().
-     */
-    public void callActivityOnCreate(Activity activity, Bundle icicle) {
+    private void prePerformCreate(Activity activity) {
         if (mWaitingActivities != null) {
             synchronized (mSync) {
                 final int N = mWaitingActivities.size();
@@ -1083,9 +1076,9 @@
                 }
             }
         }
-        
-        activity.performCreate(icicle);
-        
+    }
+
+    private void postPerformCreate(Activity activity) {
         if (mActivityMonitors != null) {
             synchronized (mSync) {
                 final int N = mActivityMonitors.size();
@@ -1096,6 +1089,33 @@
             }
         }
     }
+
+    /**
+     * Perform calling of an activity's {@link Activity#onCreate}
+     * method.  The default implementation simply calls through to that method.
+     *
+     * @param activity The activity being created.
+     * @param icicle The previously frozen state (or null) to pass through to onCreate().
+     */
+    public void callActivityOnCreate(Activity activity, Bundle icicle) {
+        prePerformCreate(activity);
+        activity.performCreate(icicle);
+        postPerformCreate(activity);
+    }
+
+    /**
+     * Perform calling of an activity's {@link Activity#onCreate}
+     * method.  The default implementation simply calls through to that method.
+     *  @param activity The activity being created.
+     * @param icicle The previously frozen state (or null) to pass through to
+     * @param persistentState The previously persisted state (or null)
+     */
+    public void callActivityOnCreate(Activity activity, Bundle icicle,
+            PersistableBundle persistentState) {
+        prePerformCreate(activity);
+        activity.performCreate(icicle, persistentState);
+        postPerformCreate(activity);
+    }
     
     public void callActivityOnDestroy(Activity activity) {
       // TODO: the following block causes intermittent hangs when using startActivity
@@ -1130,7 +1150,7 @@
     /**
      * Perform calling of an activity's {@link Activity#onRestoreInstanceState}
      * method.  The default implementation simply calls through to that method.
-     * 
+     *
      * @param activity The activity being restored.
      * @param savedInstanceState The previously saved state being restored.
      */
@@ -1139,9 +1159,22 @@
     }
 
     /**
+     * Perform calling of an activity's {@link Activity#onRestoreInstanceState}
+     * method.  The default implementation simply calls through to that method.
+     *
+     * @param activity The activity being restored.
+     * @param savedInstanceState The previously saved state being restored.
+     * @param persistentState The previously persisted state (or null)
+     */
+    public void callActivityOnRestoreInstanceState(Activity activity, Bundle savedInstanceState,
+            PersistableBundle persistentState) {
+        activity.performRestoreInstanceState(savedInstanceState, persistentState);
+    }
+
+    /**
      * Perform calling of an activity's {@link Activity#onPostCreate} method.
      * The default implementation simply calls through to that method.
-     * 
+     *
      * @param activity The activity being created.
      * @param icicle The previously frozen state (or null) to pass through to
      *               onPostCreate().
@@ -1151,6 +1184,19 @@
     }
 
     /**
+     * Perform calling of an activity's {@link Activity#onPostCreate} method.
+     * The default implementation simply calls through to that method.
+     *
+     * @param activity The activity being created.
+     * @param icicle The previously frozen state (or null) to pass through to
+     *               onPostCreate().
+     */
+    public void callActivityOnPostCreate(Activity activity, Bundle icicle,
+            PersistableBundle persistentState) {
+        activity.onPostCreate(icicle, persistentState);
+    }
+
+    /**
      * Perform calling of an activity's {@link Activity#onNewIntent}
      * method.  The default implementation simply calls through to that method.
      * 
@@ -1215,7 +1261,7 @@
     /**
      * Perform calling of an activity's {@link Activity#onSaveInstanceState}
      * method.  The default implementation simply calls through to that method.
-     * 
+     *
      * @param activity The activity being saved.
      * @param outState The bundle to pass to the call.
      */
@@ -1224,6 +1270,18 @@
     }
 
     /**
+     * Perform calling of an activity's {@link Activity#onSaveInstanceState}
+     * method.  The default implementation simply calls through to that method.
+     *  @param activity The activity being saved.
+     * @param outState The bundle to pass to the call.
+     * @param outPersistentState The persistent bundle to pass to the call.
+     */
+    public void callActivityOnSaveInstanceState(Activity activity, Bundle outState,
+            PersistableBundle outPersistentState) {
+        activity.performSaveInstanceState(outState, outPersistentState);
+    }
+
+    /**
      * Perform calling of an activity's {@link Activity#onPause} method.  The
      * default implementation simply calls through to that method.
      * 
@@ -1428,7 +1486,7 @@
     }
 
     /**
-     * Like {@link #execStartActivity},
+     * Like {@link #execStartActivity(Context, IBinder, IBinder, Activity, Intent, int, Bundle)},
      * but accepts an array of activities to be started.  Note that active
      * {@link ActivityMonitor} objects only match against the first activity in
      * the array.
@@ -1442,7 +1500,7 @@
     }
 
     /**
-     * Like {@link #execStartActivity},
+     * Like {@link #execStartActivity(Context, IBinder, IBinder, Activity, Intent, int, Bundle)},
      * but accepts an array of activities to be started.  Note that active
      * {@link ActivityMonitor} objects only match against the first activity in
      * the array.
@@ -1545,7 +1603,8 @@
     }
 
     /**
-     * Like {@link #execStartActivity}, but for starting as a particular user.
+     * Like {@link #execStartActivity(Context, IBinder, IBinder, Activity, Intent, int, Bundle)},
+     * but for starting as a particular user.
      *
      * @param who The Context from which the activity is being started.
      * @param contextThread The main thread of the Context from which the activity
diff --git a/core/java/android/app/KeyguardManager.java b/core/java/android/app/KeyguardManager.java
index aab6ed8..db91742a 100644
--- a/core/java/android/app/KeyguardManager.java
+++ b/core/java/android/app/KeyguardManager.java
@@ -44,8 +44,8 @@
      * you to disable / reenable the keyguard.
      */
     public class KeyguardLock {
-        private IBinder mToken = new Binder();
-        private String mTag;
+        private final IBinder mToken = new Binder();
+        private final String mTag;
 
         KeyguardLock(String tag) {
             mTag = tag;
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index bba6caf..76a6a8e 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -659,8 +659,8 @@
 
     /**
      * @hide
-     * Extra added by NotificationManagerService to indicate whether a NotificationScorer
-     * modified the Notifications's score.
+     * Extra added by NotificationManagerService to indicate whether
+     * the Notifications's score has been modified.
      */
     public static final String EXTRA_SCORE_MODIFIED = "android.scoreModified";
 
diff --git a/core/java/android/app/UiAutomation.java b/core/java/android/app/UiAutomation.java
index 9405325..64e3484 100644
--- a/core/java/android/app/UiAutomation.java
+++ b/core/java/android/app/UiAutomation.java
@@ -26,6 +26,7 @@
 import android.graphics.Point;
 import android.hardware.display.DisplayManagerGlobal;
 import android.os.Looper;
+import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.util.Log;
@@ -40,7 +41,9 @@
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.accessibility.AccessibilityWindowInfo;
 import android.view.accessibility.IAccessibilityInteractionConnection;
+import libcore.io.IoUtils;
 
+import java.io.IOException;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.TimeoutException;
@@ -840,6 +843,44 @@
         return null;
     }
 
+    /**
+     * Executes a shell command. This method returs a file descriptor that points
+     * to the standard output stream. The command execution is similar to running
+     * "adb shell <command>" from a host connected to the device.
+     * <p>
+     * <strong>Note:</strong> It is your responsibility to close the retunred file
+     * descriptor once you are done reading.
+     * </p>
+     *
+     * @param command The command to execute.
+     * @return A file descriptor to the standard output stream.
+     */
+    public ParcelFileDescriptor executeShellCommand(String command) {
+        synchronized (mLock) {
+            throwIfNotConnectedLocked();
+        }
+
+        ParcelFileDescriptor source = null;
+        ParcelFileDescriptor sink = null;
+
+        try {
+            ParcelFileDescriptor[] pipe = ParcelFileDescriptor.createPipe();
+            source = pipe[0];
+            sink = pipe[1];
+
+            // Calling out without a lock held.
+            mUiAutomationConnection.executeShellCommand(command, sink);
+        } catch (IOException ioe) {
+            Log.e(LOG_TAG, "Error executing shell command!", ioe);
+        } catch (RemoteException re) {
+            Log.e(LOG_TAG, "Error executing shell command!", re);
+        } finally {
+            IoUtils.closeQuietly(sink);
+        }
+
+        return source;
+    }
+
     private static float getDegreesForRotation(int value) {
         switch (value) {
             case Surface.ROTATION_90: {
diff --git a/core/java/android/app/UiAutomationConnection.java b/core/java/android/app/UiAutomationConnection.java
index fa40286..81bcb39 100644
--- a/core/java/android/app/UiAutomationConnection.java
+++ b/core/java/android/app/UiAutomationConnection.java
@@ -23,6 +23,7 @@
 import android.hardware.input.InputManager;
 import android.os.Binder;
 import android.os.IBinder;
+import android.os.ParcelFileDescriptor;
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.ServiceManager;
@@ -33,6 +34,12 @@
 import android.view.WindowContentFrameStats;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.IAccessibilityManager;
+import libcore.io.IoUtils;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
 
 /**
  * This is a remote object that is passed from the shell to an instrumentation
@@ -50,8 +57,8 @@
     private final IWindowManager mWindowManager = IWindowManager.Stub.asInterface(
             ServiceManager.getService(Service.WINDOW_SERVICE));
 
-    private final IAccessibilityManager mAccessibilityManager = IAccessibilityManager.Stub.asInterface(
-            ServiceManager.getService(Service.ACCESSIBILITY_SERVICE));
+    private final IAccessibilityManager mAccessibilityManager = IAccessibilityManager.Stub
+            .asInterface(ServiceManager.getService(Service.ACCESSIBILITY_SERVICE));
 
     private final Object mLock = new Object();
 
@@ -220,6 +227,41 @@
     }
 
     @Override
+    public void executeShellCommand(String command, ParcelFileDescriptor sink)
+            throws RemoteException {
+        synchronized (mLock) {
+            throwIfCalledByNotTrustedUidLocked();
+            throwIfShutdownLocked();
+            throwIfNotConnectedLocked();
+        }
+
+        InputStream in = null;
+        OutputStream out = null;
+
+        try {
+            java.lang.Process process = Runtime.getRuntime().exec(command);
+
+            in = process.getInputStream();
+            out = new FileOutputStream(sink.getFileDescriptor());
+
+            final byte[] buffer = new byte[8192];
+            while (true) {
+                final int readByteCount = in.read(buffer);
+                if (readByteCount < 0) {
+                    break;
+                }
+                out.write(buffer, 0, readByteCount);
+            }
+        } catch (IOException ioe) {
+            throw new RuntimeException("Error running shell command", ioe);
+        } finally {
+            IoUtils.closeQuietly(in);
+            IoUtils.closeQuietly(out);
+            IoUtils.closeQuietly(sink);
+        }
+    }
+
+    @Override
     public void shutdown() {
         synchronized (mLock) {
             if (isConnectedLocked()) {
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 24bb2cc..8884446 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -16,8 +16,6 @@
 
 package android.app.admin;
 
-import org.xmlpull.v1.XmlPullParserException;
-
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
 import android.content.ComponentName;
@@ -39,6 +37,8 @@
 
 import com.android.org.conscrypt.TrustedCertificateStore;
 
+import org.xmlpull.v1.XmlPullParserException;
+
 import java.io.ByteArrayInputStream;
 import java.io.IOException;
 import java.net.InetSocketAddress;
@@ -359,8 +359,8 @@
     }
 
     /**
-     * Retrieve the current minimum password quality for all admins
-     * or a particular one.
+     * Retrieve the current minimum password quality for all admins of this user
+     * and its profiles or a particular one.
      * @param admin The name of the admin component to check, or null to aggregate
      * all admins.
      */
@@ -412,8 +412,8 @@
     }
 
     /**
-     * Retrieve the current minimum password length for all admins
-     * or a particular one.
+     * Retrieve the current minimum password length for all admins of this
+     * user and its profiles or a particular one.
      * @param admin The name of the admin component to check, or null to aggregate
      * all admins.
      */
@@ -467,8 +467,9 @@
 
     /**
      * Retrieve the current number of upper case letters required in the
-     * password for all admins or a particular one. This is the same value as
-     * set by {#link {@link #setPasswordMinimumUpperCase(ComponentName, int)}
+     * password for all admins of this user and its profiles or a particular one.
+     * This is the same value as set by
+     * {#link {@link #setPasswordMinimumUpperCase(ComponentName, int)}
      * and only applies when the password quality is
      * {@link #PASSWORD_QUALITY_COMPLEX}.
      *
@@ -527,8 +528,9 @@
 
     /**
      * Retrieve the current number of lower case letters required in the
-     * password for all admins or a particular one. This is the same value as
-     * set by {#link {@link #setPasswordMinimumLowerCase(ComponentName, int)}
+     * password for all admins of this user and its profiles or a particular one.
+     * This is the same value as set by
+     * {#link {@link #setPasswordMinimumLowerCase(ComponentName, int)}
      * and only applies when the password quality is
      * {@link #PASSWORD_QUALITY_COMPLEX}.
      *
@@ -644,8 +646,9 @@
 
     /**
      * Retrieve the current number of numerical digits required in the password
-     * for all admins or a particular one. This is the same value as
-     * set by {#link {@link #setPasswordMinimumNumeric(ComponentName, int)}
+     * for all admins of this user and its profiles or a particular one.
+     * This is the same value as set by
+     * {#link {@link #setPasswordMinimumNumeric(ComponentName, int)}
      * and only applies when the password quality is
      * {@link #PASSWORD_QUALITY_COMPLEX}.
      *
@@ -760,8 +763,9 @@
 
     /**
      * Retrieve the current number of non-letter characters required in the
-     * password for all admins or a particular one. This is the same value as
-     * set by {#link {@link #setPasswordMinimumNonLetter(ComponentName, int)}
+     * password for all admins of this user and its profiles or a particular one.
+     * This is the same value as set by
+     * {#link {@link #setPasswordMinimumNonLetter(ComponentName, int)}
      * and only applies when the password quality is
      * {@link #PASSWORD_QUALITY_COMPLEX}.
      *
@@ -868,9 +872,10 @@
 
     /**
      * Get the current password expiration time for the given admin or an aggregate of
-     * all admins if admin is null. If the password is expired, this will return the time since
-     * the password expired as a negative number.  If admin is null, then a composite of all
-     * expiration timeouts is returned - which will be the minimum of all timeouts.
+     * all admins of this user and its profiles if admin is null. If the password is
+     * expired, this will return the time since the password expired as a negative number.
+     * If admin is null, then a composite of all expiration timeouts is returned
+     * - which will be the minimum of all timeouts.
      *
      * @param admin The name of the admin component to check, or null to aggregate all admins.
      * @return The password expiration time, in ms.
@@ -887,8 +892,8 @@
     }
 
     /**
-     * Retrieve the current password history length for all admins
-     * or a particular one.
+     * Retrieve the current password history length for all admins of this
+     * user and its profiles or a particular one.
      * @param admin The name of the admin component to check, or null to aggregate
      * all admins.
      * @return The length of the password history
@@ -923,14 +928,13 @@
     /**
      * Determine whether the current password the user has set is sufficient
      * to meet the policy requirements (quality, minimum length) that have been
-     * requested.
+     * requested by the admins of this user and its profiles.
      *
      * <p>The calling device admin must have requested
      * {@link DeviceAdminInfo#USES_POLICY_LIMIT_PASSWORD} to be able to call
      * this method; if it has not, a security exception will be thrown.
      *
-     * @return Returns true if the password meets the current requirements,
-     * else false.
+     * @return Returns true if the password meets the current requirements, else false.
      */
     public boolean isActivePasswordSufficient() {
         if (mService != null) {
@@ -993,7 +997,7 @@
 
     /**
      * Retrieve the current maximum number of login attempts that are allowed
-     * before the device wipes itself, for all admins
+     * before the device wipes itself, for all admins of this user and its profiles
      * or a particular one.
      * @param admin The name of the admin component to check, or null to aggregate
      * all admins.
@@ -1037,6 +1041,8 @@
      * {@link DeviceAdminInfo#USES_POLICY_RESET_PASSWORD} to be able to call
      * this method; if it has not, a security exception will be thrown.
      *
+     * Can not be called from a managed profile.
+     *
      * @param password The new password for the user.
      * @param flags May be 0 or {@link #RESET_PASSWORD_REQUIRE_ENTRY}.
      * @return Returns true if the password was applied, or false if it is
@@ -1077,8 +1083,8 @@
     }
 
     /**
-     * Retrieve the current maximum time to unlock for all admins
-     * or a particular one.
+     * Retrieve the current maximum time to unlock for all admins of this user
+     * and its profiles or a particular one.
      * @param admin The name of the admin component to check, or null to aggregate
      * all admins.
      */
@@ -2062,28 +2068,6 @@
     }
 
     /**
-     * Called by a profile owner to disable account management for a specific type of account.
-     *
-     * <p>The calling device admin must be a profile owner. If it is not, a
-     * security exception will be thrown.
-     *
-     * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
-     * @param accountType For which account management is disabled or enabled.
-     * @param disabled The boolean indicating that account management will be disabled (true) or
-     * enabled (false).
-     */
-    public void setAccountManagementDisabled(ComponentName admin, String accountType,
-            boolean disabled) {
-        if (mService != null) {
-            try {
-                mService.setAccountManagementDisabled(admin, accountType, disabled);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed talking with device policy service", e);
-            }
-        }
-    }
-
-    /**
      * Called by profile or device owner to re-enable system apps by intent that were disabled
      * by default when the managed profile was created. This should only be called from a profile
      * or device owner running within a managed profile.
@@ -2105,6 +2089,31 @@
     }
 
     /**
+     * Called by a profile owner to disable account management for a specific type of account.
+     *
+     * <p>The calling device admin must be a profile owner. If it is not, a
+     * security exception will be thrown.
+     *
+     * <p>When account management is disabled for an account type, adding or removing an account
+     * of that type will not be possible.
+     *
+     * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+     * @param accountType For which account management is disabled or enabled.
+     * @param disabled The boolean indicating that account management will be disabled (true) or
+     * enabled (false).
+     */
+    public void setAccountManagementDisabled(ComponentName admin, String accountType,
+            boolean disabled) {
+        if (mService != null) {
+            try {
+                mService.setAccountManagementDisabled(admin, accountType, disabled);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed talking with device policy service", e);
+            }
+        }
+    }
+
+    /**
      * Gets the array of accounts for which account management is disabled by the profile owner.
      *
      * <p> Account management can be disabled/enabled by calling
diff --git a/core/java/android/app/task/TaskParams.java b/core/java/android/app/task/TaskParams.java
index e2eafd8..0351082 100644
--- a/core/java/android/app/task/TaskParams.java
+++ b/core/java/android/app/task/TaskParams.java
@@ -29,7 +29,14 @@
 
     private final int taskId;
     private final Bundle extras;
-    private final IBinder mCallback;
+    private final IBinder callback;
+
+    /** @hide */
+    public TaskParams(int taskId, Bundle extras, IBinder callback) {
+        this.taskId = taskId;
+        this.extras = extras;
+        this.callback = callback;
+    }
 
     /**
      * @return The unique id of this task, specified at creation time.
@@ -47,17 +54,15 @@
         return extras;
     }
 
-    /**
-     * @hide
-     */
+    /** @hide */
     public ITaskCallback getCallback() {
-        return ITaskCallback.Stub.asInterface(mCallback);
+        return ITaskCallback.Stub.asInterface(callback);
     }
 
     private TaskParams(Parcel in) {
         taskId = in.readInt();
         extras = in.readBundle();
-        mCallback = in.readStrongBinder();
+        callback = in.readStrongBinder();
     }
 
     @Override
@@ -69,7 +74,7 @@
     public void writeToParcel(Parcel dest, int flags) {
         dest.writeInt(taskId);
         dest.writeBundle(extras);
-        dest.writeStrongBinder(mCallback);
+        dest.writeStrongBinder(callback);
     }
 
     public static final Creator<TaskParams> CREATOR = new Creator<TaskParams>() {
diff --git a/core/java/android/bluetooth/BluetoothTetheringDataTracker.java b/core/java/android/bluetooth/BluetoothTetheringDataTracker.java
index a0b603e..f0c8299 100644
--- a/core/java/android/bluetooth/BluetoothTetheringDataTracker.java
+++ b/core/java/android/bluetooth/BluetoothTetheringDataTracker.java
@@ -20,7 +20,7 @@
 import android.content.Context;
 import android.net.ConnectivityManager;
 import android.net.DhcpResults;
-import android.net.LinkCapabilities;
+import android.net.NetworkCapabilities;
 import android.net.LinkProperties;
 import android.net.NetworkInfo;
 import android.net.NetworkInfo.DetailedState;
@@ -75,7 +75,7 @@
     private BluetoothTetheringDataTracker() {
         mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_BLUETOOTH, 0, NETWORKTYPE, "");
         mLinkProperties = new LinkProperties();
-        mLinkCapabilities = new LinkCapabilities();
+        mNetworkCapabilities = new NetworkCapabilities();
 
         mNetworkInfo.setIsAvailable(false);
         setTeardownRequested(false);
@@ -242,16 +242,6 @@
         }
     }
 
-   /**
-     * A capability is an Integer/String pair, the capabilities
-     * are defined in the class LinkSocket#Key.
-     *
-     * @return a copy of this connections capabilities, may be empty but never null.
-     */
-    public LinkCapabilities getLinkCapabilities() {
-        return new LinkCapabilities(mLinkCapabilities);
-    }
-
     /**
      * Fetch default gateway address for the network
      */
diff --git a/core/java/android/content/SharedPreferences.java b/core/java/android/content/SharedPreferences.java
index d4f7f06..6b4404d 100644
--- a/core/java/android/content/SharedPreferences.java
+++ b/core/java/android/content/SharedPreferences.java
@@ -82,7 +82,7 @@
         
         /**
          * Set a set of String values in the preferences editor, to be written
-         * back once {@link #commit} is called.
+         * back once {@link #commit} or {@link #apply} is called.
          * 
          * @param key The name of the preference to modify.
          * @param values The set of new values for the preference.  Passing {@code null}
diff --git a/core/java/android/content/Task.java b/core/java/android/content/Task.java
index ed5ed884..407880f 100644
--- a/core/java/android/content/Task.java
+++ b/core/java/android/content/Task.java
@@ -42,6 +42,20 @@
         public final int EXPONENTIAL = 1;
     }
 
+    private final int taskId;
+    // TODO: Change this to use PersistableBundle when that lands in master.
+    private final Bundle extras;
+    private final ComponentName service;
+    private final boolean requireCharging;
+    private final boolean requireDeviceIdle;
+    private final int networkCapabilities;
+    private final long minLatencyMillis;
+    private final long maxExecutionDelayMillis;
+    private final boolean isPeriodic;
+    private final long intervalMillis;
+    private final long initialBackoffMillis;
+    private final int backoffPolicy;
+
     /**
      * Unique task id associated with this class. This is assigned to your task by the scheduler.
      */
@@ -59,8 +73,8 @@
     /**
      * Name of the service endpoint that will be called back into by the TaskManager.
      */
-    public String getServiceClassName() {
-        return serviceClassName;
+    public ComponentName getService() {
+        return service;
     }
 
     /**
@@ -132,24 +146,10 @@
         return backoffPolicy;
     }
 
-    private final int taskId;
-    // TODO: Change this to use PersistableBundle when that lands in master.
-    private final Bundle extras;
-    private final String serviceClassName;
-    private final boolean requireCharging;
-    private final boolean requireDeviceIdle;
-    private final int networkCapabilities;
-    private final long minLatencyMillis;
-    private final long maxExecutionDelayMillis;
-    private final boolean isPeriodic;
-    private final long intervalMillis;
-    private final long initialBackoffMillis;
-    private final int backoffPolicy;
-
     private Task(Parcel in) {
         taskId = in.readInt();
         extras = in.readBundle();
-        serviceClassName = in.readString();
+        service = ComponentName.readFromParcel(in);
         requireCharging = in.readInt() == 1;
         requireDeviceIdle = in.readInt() == 1;
         networkCapabilities = in.readInt();
@@ -164,7 +164,7 @@
     private Task(Task.Builder b) {
         taskId = b.mTaskId;
         extras = new Bundle(b.mExtras);
-        serviceClassName = b.mTaskServiceClassName;
+        service = b.mTaskService;
         requireCharging = b.mRequiresCharging;
         requireDeviceIdle = b.mRequiresDeviceIdle;
         networkCapabilities = b.mNetworkCapabilities;
@@ -185,7 +185,7 @@
     public void writeToParcel(Parcel out, int flags) {
         out.writeInt(taskId);
         out.writeBundle(extras);
-        out.writeString(serviceClassName);
+        ComponentName.writeToParcel(service, out);
         out.writeInt(requireCharging ? 1 : 0);
         out.writeInt(requireDeviceIdle ? 1 : 0);
         out.writeInt(networkCapabilities);
@@ -215,7 +215,7 @@
     public final class Builder {
         private int mTaskId;
         private Bundle mExtras;
-        private String mTaskServiceClassName;
+        private ComponentName mTaskService;
         // Requirements.
         private boolean mRequiresCharging;
         private boolean mRequiresDeviceIdle;
@@ -236,11 +236,11 @@
          * @param taskId Application-provided id for this task. Subsequent calls to cancel, or
          *               tasks created with the same taskId, will update the pre-existing task with
          *               the same id.
-         * @param cls The endpoint that you implement that will receive the callback from the
+         * @param taskService The endpoint that you implement that will receive the callback from the
          *            TaskManager.
          */
-        public Builder(int taskId, Class<TaskService> cls) {
-            mTaskServiceClassName = cls.getClass().getName();
+        public Builder(int taskId, ComponentName taskService) {
+            mTaskService = taskService;
             mTaskId = taskId;
         }
 
@@ -296,7 +296,7 @@
          * period. You have no control over when within this interval this task will be executed,
          * only the guarantee that it will be executed at most once within this interval.
          * A periodic task will be repeated until the phone is turned off, however it will only be
-         * persisted if the client app has declared the
+         * persisted beyond boot if the client app has declared the
          * {@link android.Manifest.permission#RECEIVE_BOOT_COMPLETED} permission. You can schedule
          * periodic tasks without this permission, they simply will cease to exist after the phone
          * restarts.
diff --git a/core/java/android/hardware/Sensor.java b/core/java/android/hardware/Sensor.java
index 4bea9ee..86208fc 100644
--- a/core/java/android/hardware/Sensor.java
+++ b/core/java/android/hardware/Sensor.java
@@ -397,6 +397,32 @@
     public static final String STRING_TYPE_HEART_RATE = "android.sensor.heart_rate";
 
     /**
+     * A constant describing a wake gesture sensor.
+     * <p>
+     * Wake gesture sensors enable waking up the device based on a device specific motion.
+     * <p>
+     * When this sensor triggers, the device behaves as if the power button was pressed, turning the
+     * screen on. This behavior (turning on the screen when this sensor triggers) might be
+     * deactivated by the user in the device settings. Changes in settings do not impact the
+     * behavior of the sensor: only whether the framework turns the screen on when it triggers.
+     * <p>
+     * The actual gesture to be detected is not specified, and can be chosen by the manufacturer of
+     * the device. This sensor must be low power, as it is likely to be activated 24/7.
+     * Values of events created by this sensors should not be used.
+     *
+     * @hide This sensor is expected to only be used by the power manager
+     */
+    public static final int TYPE_WAKE_GESTURE = 42;
+
+    /**
+     * A constant string describing a wake gesture sensor.
+     *
+     * @hide This sensor is expected to only be used by the power manager
+     * @see #TYPE_WAKE_GESTURE
+     */
+    public static final String STRING_TYPE_WAKE_GESTURE = "android.sensor.wake_gesture";
+
+    /**
      * A constant describing all sensor types.
      */
     public static final int TYPE_ALL = -1;
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index f161f3a..0ca9161 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -458,7 +458,19 @@
      * brightness</p>
      * <p>For example, if EV step is 0.333, '6' will mean an
      * exposure compensation of +2 EV; -3 will mean an exposure
-     * compensation of -1</p>
+     * compensation of -1 EV. Note that this control will only be effective
+     * if {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode} <code>!=</code> OFF. This control will take effect even when
+     * {@link CaptureRequest#CONTROL_AE_LOCK android.control.aeLock} <code>== true</code>.</p>
+     * <p>In the event of exposure compensation value being changed, camera device
+     * may take several frames to reach the newly requested exposure target.
+     * During that time, {@link CaptureResult#CONTROL_AE_STATE android.control.aeState} field will be in the SEARCHING
+     * state. Once the new exposure target is reached, {@link CaptureResult#CONTROL_AE_STATE android.control.aeState} will
+     * change from SEARCHING to either CONVERGED, LOCKED (if AE lock is enabled), or
+     * FLASH_REQUIRED (if the scene is too dark for still capture).</p>
+     *
+     * @see CaptureRequest#CONTROL_AE_LOCK
+     * @see CaptureRequest#CONTROL_AE_MODE
+     * @see CaptureResult#CONTROL_AE_STATE
      */
     public static final Key<Integer> CONTROL_AE_EXPOSURE_COMPENSATION =
             new Key<Integer>("android.control.aeExposureCompensation", int.class);
@@ -469,6 +481,8 @@
      * <p>Note that even when AE is locked, the flash may be
      * fired if the {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode} is ON_AUTO_FLASH / ON_ALWAYS_FLASH /
      * ON_AUTO_FLASH_REDEYE.</p>
+     * <p>When {@link CaptureRequest#CONTROL_AE_EXPOSURE_COMPENSATION android.control.aeExposureCompensation} is changed, even if the AE lock
+     * is ON, the camera device will still adjust its exposure value.</p>
      * <p>If AE precapture is triggered (see {@link CaptureRequest#CONTROL_AE_PRECAPTURE_TRIGGER android.control.aePrecaptureTrigger})
      * when AE is already locked, the camera device will not change the exposure time
      * ({@link CaptureRequest#SENSOR_EXPOSURE_TIME android.sensor.exposureTime}) and sensitivity ({@link CaptureRequest#SENSOR_SENSITIVITY android.sensor.sensitivity})
@@ -477,6 +491,7 @@
      * {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode} is ON_ALWAYS_FLASH, the scene may become overexposed.</p>
      * <p>See {@link CaptureResult#CONTROL_AE_STATE android.control.aeState} for AE lock related state transition details.</p>
      *
+     * @see CaptureRequest#CONTROL_AE_EXPOSURE_COMPENSATION
      * @see CaptureRequest#CONTROL_AE_MODE
      * @see CaptureRequest#CONTROL_AE_PRECAPTURE_TRIGGER
      * @see CaptureResult#CONTROL_AE_STATE
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index 51ea447..42a3de8 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -275,7 +275,19 @@
      * brightness</p>
      * <p>For example, if EV step is 0.333, '6' will mean an
      * exposure compensation of +2 EV; -3 will mean an exposure
-     * compensation of -1</p>
+     * compensation of -1 EV. Note that this control will only be effective
+     * if {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode} <code>!=</code> OFF. This control will take effect even when
+     * {@link CaptureRequest#CONTROL_AE_LOCK android.control.aeLock} <code>== true</code>.</p>
+     * <p>In the event of exposure compensation value being changed, camera device
+     * may take several frames to reach the newly requested exposure target.
+     * During that time, {@link CaptureResult#CONTROL_AE_STATE android.control.aeState} field will be in the SEARCHING
+     * state. Once the new exposure target is reached, {@link CaptureResult#CONTROL_AE_STATE android.control.aeState} will
+     * change from SEARCHING to either CONVERGED, LOCKED (if AE lock is enabled), or
+     * FLASH_REQUIRED (if the scene is too dark for still capture).</p>
+     *
+     * @see CaptureRequest#CONTROL_AE_LOCK
+     * @see CaptureRequest#CONTROL_AE_MODE
+     * @see CaptureResult#CONTROL_AE_STATE
      */
     public static final Key<Integer> CONTROL_AE_EXPOSURE_COMPENSATION =
             new Key<Integer>("android.control.aeExposureCompensation", int.class);
@@ -286,6 +298,8 @@
      * <p>Note that even when AE is locked, the flash may be
      * fired if the {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode} is ON_AUTO_FLASH / ON_ALWAYS_FLASH /
      * ON_AUTO_FLASH_REDEYE.</p>
+     * <p>When {@link CaptureRequest#CONTROL_AE_EXPOSURE_COMPENSATION android.control.aeExposureCompensation} is changed, even if the AE lock
+     * is ON, the camera device will still adjust its exposure value.</p>
      * <p>If AE precapture is triggered (see {@link CaptureRequest#CONTROL_AE_PRECAPTURE_TRIGGER android.control.aePrecaptureTrigger})
      * when AE is already locked, the camera device will not change the exposure time
      * ({@link CaptureRequest#SENSOR_EXPOSURE_TIME android.sensor.exposureTime}) and sensitivity ({@link CaptureRequest#SENSOR_SENSITIVITY android.sensor.sensitivity})
@@ -294,6 +308,7 @@
      * {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode} is ON_ALWAYS_FLASH, the scene may become overexposed.</p>
      * <p>See {@link CaptureResult#CONTROL_AE_STATE android.control.aeState} for AE lock related state transition details.</p>
      *
+     * @see CaptureRequest#CONTROL_AE_EXPOSURE_COMPENSATION
      * @see CaptureRequest#CONTROL_AE_MODE
      * @see CaptureRequest#CONTROL_AE_PRECAPTURE_TRIGGER
      * @see CaptureResult#CONTROL_AE_STATE
diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java
index cec90cd..e58c54d 100644
--- a/core/java/android/hardware/display/DisplayManagerInternal.java
+++ b/core/java/android/hardware/display/DisplayManagerInternal.java
@@ -156,6 +156,9 @@
         // If true, enables automatic brightness control.
         public boolean useAutoBrightness;
 
+        //If true, scales the brightness to half of desired.
+        public boolean lowPowerMode;
+
         // If true, prevents the screen from completely turning on if it is currently off.
         // The display does not enter a "ready" state if this flag is true and screen on is
         // blocked.  The window manager policy blocks screen on while it prepares the keyguard to
@@ -203,6 +206,7 @@
             screenAutoBrightnessAdjustment = other.screenAutoBrightnessAdjustment;
             useAutoBrightness = other.useAutoBrightness;
             blockScreenOn = other.blockScreenOn;
+            lowPowerMode = other.lowPowerMode;
         }
 
         @Override
@@ -218,7 +222,8 @@
                     && screenBrightness == other.screenBrightness
                     && screenAutoBrightnessAdjustment == other.screenAutoBrightnessAdjustment
                     && useAutoBrightness == other.useAutoBrightness
-                    && blockScreenOn == other.blockScreenOn;
+                    && blockScreenOn == other.blockScreenOn
+                    && lowPowerMode == other.lowPowerMode;
         }
 
         @Override
@@ -233,7 +238,8 @@
                     + ", screenBrightness=" + screenBrightness
                     + ", screenAutoBrightnessAdjustment=" + screenAutoBrightnessAdjustment
                     + ", useAutoBrightness=" + useAutoBrightness
-                    + ", blockScreenOn=" + blockScreenOn;
+                    + ", blockScreenOn=" + blockScreenOn
+                    + ", lowPowerMode=" + lowPowerMode;
         }
     }
 
diff --git a/core/java/android/net/BaseNetworkStateTracker.java b/core/java/android/net/BaseNetworkStateTracker.java
index 804f8ee..79db389 100644
--- a/core/java/android/net/BaseNetworkStateTracker.java
+++ b/core/java/android/net/BaseNetworkStateTracker.java
@@ -44,7 +44,8 @@
 
     protected NetworkInfo mNetworkInfo;
     protected LinkProperties mLinkProperties;
-    protected LinkCapabilities mLinkCapabilities;
+    protected NetworkCapabilities mNetworkCapabilities;
+    protected Network mNetwork = new Network(ConnectivityManager.INVALID_NET_ID);
 
     private AtomicBoolean mTeardownRequested = new AtomicBoolean(false);
     private AtomicBoolean mPrivateDnsRouteSet = new AtomicBoolean(false);
@@ -54,7 +55,7 @@
         mNetworkInfo = new NetworkInfo(
                 networkType, -1, ConnectivityManager.getNetworkTypeName(networkType), null);
         mLinkProperties = new LinkProperties();
-        mLinkCapabilities = new LinkCapabilities();
+        mNetworkCapabilities = new NetworkCapabilities();
     }
 
     protected BaseNetworkStateTracker() {
@@ -98,8 +99,8 @@
     }
 
     @Override
-    public LinkCapabilities getLinkCapabilities() {
-        return new LinkCapabilities(mLinkCapabilities);
+    public NetworkCapabilities getNetworkCapabilities() {
+        return new NetworkCapabilities(mNetworkCapabilities);
     }
 
     @Override
@@ -201,4 +202,14 @@
     public void stopSampling(SamplingDataTracker.SamplingSnapshot s) {
         // nothing to do
     }
+
+    @Override
+    public void setNetId(int netId) {
+        mNetwork = new Network(netId);
+    }
+
+    @Override
+    public Network getNetwork() {
+        return mNetwork;
+    }
 }
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 30d7043..3e00250 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -408,6 +408,11 @@
      */
     public static final int CONNECTIVITY_CHANGE_DELAY_DEFAULT = 3000;
 
+    /**
+     * @hide
+     */
+    public final static int INVALID_NET_ID = 0;
+
     private final IConnectivityManager mService;
 
     private final String mPackageName;
diff --git a/core/java/android/net/DummyDataStateTracker.java b/core/java/android/net/DummyDataStateTracker.java
index a5d059e..eff9f9f 100644
--- a/core/java/android/net/DummyDataStateTracker.java
+++ b/core/java/android/net/DummyDataStateTracker.java
@@ -190,13 +190,6 @@
         return new LinkProperties(mLinkProperties);
     }
 
-    /**
-     * @see android.net.NetworkStateTracker#getLinkCapabilities()
-     */
-    public LinkCapabilities getLinkCapabilities() {
-        return new LinkCapabilities(mLinkCapabilities);
-    }
-
     public void setDependencyMet(boolean met) {
         // not supported on this network
     }
diff --git a/core/java/android/net/EthernetDataTracker.java b/core/java/android/net/EthernetDataTracker.java
index 10b5d0b..c1afc9b 100644
--- a/core/java/android/net/EthernetDataTracker.java
+++ b/core/java/android/net/EthernetDataTracker.java
@@ -103,7 +103,7 @@
     private EthernetDataTracker() {
         mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_ETHERNET, 0, NETWORKTYPE, "");
         mLinkProperties = new LinkProperties();
-        mLinkCapabilities = new LinkCapabilities();
+        mNetworkCapabilities = new NetworkCapabilities();
     }
 
     private void interfaceUpdated() {
@@ -372,16 +372,6 @@
         return new LinkProperties(mLinkProperties);
     }
 
-   /**
-     * A capability is an Integer/String pair, the capabilities
-     * are defined in the class LinkSocket#Key.
-     *
-     * @return a copy of this connections capabilities, may be empty but never null.
-     */
-    public LinkCapabilities getLinkCapabilities() {
-        return new LinkCapabilities(mLinkCapabilities);
-    }
-
     /**
      * Fetch default gateway address for the network
      */
diff --git a/core/java/android/net/LinkCapabilities.java b/core/java/android/net/LinkCapabilities.java
deleted file mode 100644
index fb444ea..0000000
--- a/core/java/android/net/LinkCapabilities.java
+++ /dev/null
@@ -1,362 +0,0 @@
-/*
- * Copyright (C) 2010 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.net;
-
-import android.os.Parcelable;
-import android.os.Parcel;
-import android.util.Log;
-
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Set;
-
-/**
- * A class representing the capabilities of a link
- *
- * @hide
- */
-public class LinkCapabilities implements Parcelable {
-    private static final String TAG = "LinkCapabilities";
-    private static final boolean DBG = false;
-
-    /** The Map of Keys to Values */
-    private HashMap<Integer, String> mCapabilities;
-
-
-    /**
-     * The set of keys defined for a links capabilities.
-     *
-     * Keys starting with RW are read + write, i.e. the application
-     * can request for a certain requirement corresponding to that key.
-     * Keys starting with RO are read only, i.e. the the application
-     * can read the value of that key from the socket but cannot request
-     * a corresponding requirement.
-     *
-     * TODO: Provide a documentation technique for concisely and precisely
-     * define the syntax for each value string associated with a key.
-     */
-    public static final class Key {
-        /** No constructor */
-        private Key() {}
-
-        /**
-         * An integer representing the network type.
-         * @see ConnectivityManager
-         */
-        public final static int RO_NETWORK_TYPE = 1;
-
-        /**
-         * Desired minimum forward link (download) bandwidth for the
-         * in kilobits per second (kbps). Values should be strings such
-         * "50", "100", "1500", etc.
-         */
-        public final static int RW_DESIRED_FWD_BW = 2;
-
-        /**
-         * Required minimum forward link (download) bandwidth, in
-         * per second (kbps), below which the socket cannot function.
-         * Values should be strings such as "50", "100", "1500", etc.
-         */
-        public final static int RW_REQUIRED_FWD_BW = 3;
-
-        /**
-         * Available forward link (download) bandwidth for the socket.
-         * This value is in kilobits per second (kbps).
-         * Values will be strings such as "50", "100", "1500", etc.
-         */
-        public final static int RO_AVAILABLE_FWD_BW = 4;
-
-        /**
-         * Desired minimum reverse link (upload) bandwidth for the socket
-         * in kilobits per second (kbps).
-         * Values should be strings such as "50", "100", "1500", etc.
-         * <p>
-         * This key is set via the needs map.
-         */
-        public final static int RW_DESIRED_REV_BW = 5;
-
-        /**
-         * Required minimum reverse link (upload) bandwidth, in kilobits
-         * per second (kbps), below which the socket cannot function.
-         * If a rate is not specified, the default rate of kbps will be
-         * Values should be strings such as "50", "100", "1500", etc.
-         */
-        public final static int RW_REQUIRED_REV_BW = 6;
-
-        /**
-         * Available reverse link (upload) bandwidth for the socket.
-         * This value is in kilobits per second (kbps).
-         * Values will be strings such as "50", "100", "1500", etc.
-         */
-        public final static int RO_AVAILABLE_REV_BW = 7;
-
-        /**
-         * Maximum latency for the socket, in milliseconds, above which
-         * socket cannot function.
-         * Values should be strings such as "50", "300", "500", etc.
-         */
-        public final static int RW_MAX_ALLOWED_LATENCY = 8;
-
-        /**
-         * Interface that the socket is bound to. This can be a virtual
-         * interface (e.g. VPN or Mobile IP) or a physical interface
-         * (e.g. wlan0 or rmnet0).
-         * Values will be strings such as "wlan0", "rmnet0"
-         */
-        public final static int RO_BOUND_INTERFACE = 9;
-
-        /**
-         * Physical interface that the socket is routed on.
-         * This can be different from BOUND_INTERFACE in cases such as
-         * VPN or Mobile IP. The physical interface may change over time
-         * if seamless mobility is supported.
-         * Values will be strings such as "wlan0", "rmnet0"
-         */
-        public final static int RO_PHYSICAL_INTERFACE = 10;
-    }
-
-    /**
-     * Role informs the LinkSocket about the data usage patterns of your
-     * application.
-     * <P>
-     * {@code Role.DEFAULT} is the default role, and is used whenever
-     * a role isn't set.
-     */
-    public static final class Role {
-        /** No constructor */
-        private Role() {}
-
-        // examples only, discuss which roles should be defined, and then
-        // code these to match
-
-        /** Default Role */
-        public static final String DEFAULT = "default";
-        /** Bulk down load */
-        public static final String BULK_DOWNLOAD = "bulk.download";
-        /** Bulk upload */
-        public static final String BULK_UPLOAD = "bulk.upload";
-
-        /** VoIP Application at 24kbps */
-        public static final String VOIP_24KBPS = "voip.24k";
-        /** VoIP Application at 32kbps */
-        public static final String VOIP_32KBPS = "voip.32k";
-
-        /** Video Streaming at 480p */
-        public static final String VIDEO_STREAMING_480P = "video.streaming.480p";
-        /** Video Streaming at 720p */
-        public static final String VIDEO_STREAMING_720I = "video.streaming.720i";
-
-        /** Video Chat Application at 360p */
-        public static final String VIDEO_CHAT_360P = "video.chat.360p";
-        /** Video Chat Application at 480p */
-        public static final String VIDEO_CHAT_480P = "video.chat.480i";
-    }
-
-    /**
-     * Constructor
-     */
-    public LinkCapabilities() {
-        mCapabilities = new HashMap<Integer, String>();
-    }
-
-    /**
-     * Copy constructor.
-     *
-     * @param source
-     */
-    public LinkCapabilities(LinkCapabilities source) {
-        if (source != null) {
-            mCapabilities = new HashMap<Integer, String>(source.mCapabilities);
-        } else {
-            mCapabilities = new HashMap<Integer, String>();
-        }
-    }
-
-    /**
-     * Create the {@code LinkCapabilities} with values depending on role type.
-     * @param applicationRole a {@code LinkSocket.Role}
-     * @return the {@code LinkCapabilities} associated with the applicationRole, empty if none
-     */
-    public static LinkCapabilities createNeedsMap(String applicationRole) {
-        if (DBG) log("createNeededCapabilities(applicationRole) EX");
-        return new LinkCapabilities();
-    }
-
-    /**
-     * Remove all capabilities
-     */
-    public void clear() {
-        mCapabilities.clear();
-    }
-
-    /**
-     * Returns whether this map is empty.
-     */
-    public boolean isEmpty() {
-        return mCapabilities.isEmpty();
-    }
-
-    /**
-     * Returns the number of elements in this map.
-     *
-     * @return the number of elements in this map.
-     */
-    public int size() {
-        return mCapabilities.size();
-    }
-
-    /**
-     * Given the key return the capability string
-     *
-     * @param key
-     * @return the capability string
-     */
-    public String get(int key) {
-        return mCapabilities.get(key);
-    }
-
-    /**
-     * Store the key/value capability pair
-     *
-     * @param key
-     * @param value
-     */
-    public void put(int key, String value) {
-        mCapabilities.put(key, value);
-    }
-
-    /**
-     * Returns whether this map contains the specified key.
-     *
-     * @param key to search for.
-     * @return {@code true} if this map contains the specified key,
-     *         {@code false} otherwise.
-     */
-    public boolean containsKey(int key) {
-        return mCapabilities.containsKey(key);
-    }
-
-    /**
-     * Returns whether this map contains the specified value.
-     *
-     * @param value to search for.
-     * @return {@code true} if this map contains the specified value,
-     *         {@code false} otherwise.
-     */
-    public boolean containsValue(String value) {
-        return mCapabilities.containsValue(value);
-    }
-
-    /**
-     * Returns a set containing all of the mappings in this map. Each mapping is
-     * an instance of {@link Map.Entry}. As the set is backed by this map,
-     * changes in one will be reflected in the other.
-     *
-     * @return a set of the mappings.
-     */
-    public Set<Entry<Integer, String>> entrySet() {
-        return mCapabilities.entrySet();
-    }
-
-    /**
-     * @return the set of the keys.
-     */
-    public Set<Integer> keySet() {
-        return mCapabilities.keySet();
-    }
-
-    /**
-     * @return the set of values
-     */
-    public Collection<String> values() {
-        return mCapabilities.values();
-    }
-
-    /**
-     * Implement the Parcelable interface
-     * @hide
-     */
-    public int describeContents() {
-        return 0;
-    }
-
-    /**
-     * Convert to string for debugging
-     */
-    @Override
-    public String toString() {
-        StringBuilder sb = new StringBuilder();
-        sb.append("{");
-        boolean firstTime = true;
-        for (Entry<Integer, String> entry : mCapabilities.entrySet()) {
-            if (firstTime) {
-                firstTime = false;
-            } else {
-                sb.append(",");
-            }
-            sb.append(entry.getKey());
-            sb.append(":\"");
-            sb.append(entry.getValue());
-            sb.append("\"");
-        }
-        sb.append("}");
-        return sb.toString();
-    }
-
-    /**
-     * Implement the Parcelable interface.
-     * @hide
-     */
-    public void writeToParcel(Parcel dest, int flags) {
-        dest.writeInt(mCapabilities.size());
-        for (Entry<Integer, String> entry : mCapabilities.entrySet()) {
-            dest.writeInt(entry.getKey().intValue());
-            dest.writeString(entry.getValue());
-        }
-    }
-
-    /**
-     * Implement the Parcelable interface.
-     * @hide
-     */
-    public static final Creator<LinkCapabilities> CREATOR =
-        new Creator<LinkCapabilities>() {
-            public LinkCapabilities createFromParcel(Parcel in) {
-                LinkCapabilities capabilities = new LinkCapabilities();
-                int size = in.readInt();
-                while (size-- != 0) {
-                    int key = in.readInt();
-                    String value = in.readString();
-                    capabilities.mCapabilities.put(key, value);
-                }
-                return capabilities;
-            }
-
-            public LinkCapabilities[] newArray(int size) {
-                return new LinkCapabilities[size];
-            }
-        };
-
-    /**
-     * Debug logging
-     */
-    protected static void log(String s) {
-        Log.d(TAG, s);
-    }
-}
diff --git a/core/java/android/net/LinkSocket.java b/core/java/android/net/LinkSocket.java
deleted file mode 100644
index 5aa6451..0000000
--- a/core/java/android/net/LinkSocket.java
+++ /dev/null
@@ -1,276 +0,0 @@
-/*
- * Copyright (C) 2010 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.net;
-
-import android.net.LinkCapabilities;
-import android.net.LinkProperties;
-import android.net.LinkSocketNotifier;
-
-import android.util.Log;
-
-import java.io.IOException;
-import java.net.Socket;
-import java.net.SocketAddress;
-import java.net.SocketTimeoutException;
-import java.net.UnknownHostException;
-import java.util.HashSet;
-import java.util.Set;
-
-/** @hide */
-public class LinkSocket extends Socket {
-    private final static String TAG = "LinkSocket";
-    private final static boolean DBG = true;
-
-    /**
-     * Default constructor
-     */
-    public LinkSocket() {
-        if (DBG) log("LinkSocket() EX");
-    }
-
-    /**
-     * Creates a new unconnected socket.
-     * @param notifier a reference to a class that implements {@code LinkSocketNotifier}
-     */
-    public LinkSocket(LinkSocketNotifier notifier) {
-        if (DBG) log("LinkSocket(notifier) EX");
-    }
-
-    /**
-     * Creates a new unconnected socket usign the given proxy type.
-     * @param notifier a reference to a class that implements {@code LinkSocketNotifier}
-     * @param proxy the specified proxy for this socket
-     * @throws IllegalArgumentException if the argument proxy is null or of an invalid type.
-     * @throws SecurityException if a security manager exists and it denies the permission
-     *                           to connect to the given proxy.
-     */
-    public LinkSocket(LinkSocketNotifier notifier, Proxy proxy) {
-        if (DBG) log("LinkSocket(notifier, proxy) EX");
-    }
-
-    /**
-     * @return the {@code LinkProperties} for the socket
-     */
-    public LinkProperties getLinkProperties() {
-        if (DBG) log("LinkProperties() EX");
-        return new LinkProperties();
-    }
-
-    /**
-     * Set the {@code LinkCapabilies} needed for this socket.  If the socket is already connected
-     * or is a duplicate socket the request is ignored and {@code false} will
-     * be returned. A needs map can be created via the {@code createNeedsMap} static
-     * method.
-     * @param needs the needs of the socket
-     * @return {@code true} if needs are successfully set, {@code false} otherwise
-     */
-    public boolean setNeededCapabilities(LinkCapabilities needs) {
-        if (DBG) log("setNeeds() EX");
-        return false;
-    }
-
-    /**
-     * @return the LinkCapabilites set by setNeededCapabilities, empty if none has been set
-     */
-    public LinkCapabilities getNeededCapabilities() {
-        if (DBG) log("getNeeds() EX");
-        return null;
-    }
-
-    /**
-     * @return all of the {@code LinkCapabilities} of the link used by this socket
-     */
-    public LinkCapabilities getCapabilities() {
-        if (DBG) log("getCapabilities() EX");
-        return null;
-    }
-
-    /**
-     * Returns this LinkSockets set of capabilities, filtered according to
-     * the given {@code Set}.  Capabilities in the Set but not available from
-     * the link will not be reported in the results.  Capabilities of the link
-     * but not listed in the Set will also not be reported in the results.
-     * @param capabilities {@code Set} of capabilities requested
-     * @return the filtered {@code LinkCapabilities} of this LinkSocket, may be empty
-     */
-    public LinkCapabilities getCapabilities(Set<Integer> capabilities) {
-        if (DBG) log("getCapabilities(capabilities) EX");
-        return new LinkCapabilities();
-    }
-
-    /**
-     * Provide the set of capabilities the application is interested in tracking
-     * for this LinkSocket.
-     * @param capabilities a {@code Set} of capabilities to track
-     */
-    public void setTrackedCapabilities(Set<Integer> capabilities) {
-        if (DBG) log("setTrackedCapabilities(capabilities) EX");
-    }
-
-    /**
-     * @return the {@code LinkCapabilities} that are tracked, empty if none has been set.
-     */
-    public Set<Integer> getTrackedCapabilities() {
-        if (DBG) log("getTrackedCapabilities(capabilities) EX");
-        return new HashSet<Integer>();
-    }
-
-    /**
-     * Connects this socket to the given remote host address and port specified
-     * by dstName and dstPort.
-     * @param dstName the address of the remote host to connect to
-     * @param dstPort the port to connect to on the remote host
-     * @param timeout the timeout value in milliseconds or 0 for infinite timeout
-     * @throws UnknownHostException if the given dstName is invalid
-     * @throws IOException if the socket is already connected or an error occurs
-     *                     while connecting
-     * @throws SocketTimeoutException if the timeout fires
-     */
-    public void connect(String dstName, int dstPort, int timeout)
-            throws UnknownHostException, IOException, SocketTimeoutException {
-        if (DBG) log("connect(dstName, dstPort, timeout) EX");
-    }
-
-    /**
-     * Connects this socket to the given remote host address and port specified
-     * by dstName and dstPort.
-     * @param dstName the address of the remote host to connect to
-     * @param dstPort the port to connect to on the remote host
-     * @throws UnknownHostException if the given dstName is invalid
-     * @throws IOException if the socket is already connected or an error occurs
-     *                     while connecting
-     */
-    public void connect(String dstName, int dstPort)
-            throws UnknownHostException, IOException {
-        if (DBG) log("connect(dstName, dstPort, timeout) EX");
-    }
-
-    /**
-     * Connects this socket to the given remote host address and port specified
-     * by the SocketAddress with the specified timeout.
-     * @deprecated Use {@code connect(String dstName, int dstPort, int timeout)}
-     *             instead.  Using this method may result in reduced functionality.
-     * @param remoteAddr the address and port of the remote host to connect to
-     * @throws IllegalArgumentException if the given SocketAddress is invalid
-     * @throws IOException if the socket is already connected or an error occurs
-     *                     while connecting
-     * @throws SocketTimeoutException if the timeout expires
-     */
-    @Override
-    @Deprecated
-    public void connect(SocketAddress remoteAddr, int timeout)
-            throws IOException, SocketTimeoutException {
-        if (DBG) log("connect(remoteAddr, timeout) EX DEPRECATED");
-    }
-
-    /**
-     * Connects this socket to the given remote host address and port specified
-     * by the SocketAddress.
-     * TODO add comment on all these that the network selection happens during connect
-     * and may take 30 seconds
-     * @deprecated Use {@code connect(String dstName, int dstPort)}
-     *             Using this method may result in reduced functionality.
-     * @param remoteAddr the address and port of the remote host to connect to.
-     * @throws IllegalArgumentException if the SocketAddress is invalid or not supported.
-     * @throws IOException if the socket is already connected or an error occurs
-     *                     while connecting
-     */
-    @Override
-    @Deprecated
-    public void connect(SocketAddress remoteAddr) throws IOException {
-        if (DBG) log("connect(remoteAddr) EX DEPRECATED");
-    }
-
-    /**
-     * Connect a duplicate socket socket to the same remote host address and port
-     * as the original with a timeout parameter.
-     * @param timeout the timeout value in milliseconds or 0 for infinite timeout
-     * @throws IOException if the socket is already connected or an error occurs
-     *                     while connecting
-     */
-    public void connect(int timeout) throws IOException {
-        if (DBG) log("connect(timeout) EX");
-    }
-
-    /**
-     * Connect a duplicate socket socket to the same remote host address and port
-     * as the original.
-     * @throws IOException if the socket is already connected or an error occurs
-     *                     while connecting
-     */
-    public void connect() throws IOException {
-        if (DBG) log("connect() EX");
-    }
-
-    /**
-     * Closes the socket.  It is not possible to reconnect or rebind to this
-     * socket thereafter which means a new socket instance has to be created.
-     * @throws IOException if an error occurs while closing the socket
-     */
-    @Override
-    public synchronized void close() throws IOException {
-        if (DBG) log("close() EX");
-    }
-
-    /**
-     * Request that a new LinkSocket be created using a different radio
-     * (such as WiFi or 3G) than the current LinkSocket.  If a different
-     * radio is available a call back will be made via {@code onBetterLinkAvail}.
-     * If unable to find a better radio, application will be notified via
-     * {@code onNewLinkUnavailable}
-     * @see LinkSocketNotifier#onBetterLinkAvailable(LinkSocket, LinkSocket)
-     * @param linkRequestReason reason for requesting a new link.
-     */
-    public void requestNewLink(LinkRequestReason linkRequestReason) {
-        if (DBG) log("requestNewLink(linkRequestReason) EX");
-    }
-
-    /**
-     * @deprecated LinkSocket will automatically pick the optimum interface
-     *             to bind to
-     * @param localAddr the specific address and port on the local machine
-     *                  to bind to
-     * @throws IOException always as this method is deprecated for LinkSocket
-     */
-    @Override
-    @Deprecated
-    public void bind(SocketAddress localAddr) throws UnsupportedOperationException {
-        if (DBG) log("bind(localAddr) EX throws IOException");
-        throw new UnsupportedOperationException("bind is deprecated for LinkSocket");
-    }
-
-    /**
-     * Reason codes an application can specify when requesting for a new link.
-     * TODO: need better documentation
-     */
-    public static final class LinkRequestReason {
-        /** No constructor */
-        private LinkRequestReason() {}
-
-        /** This link is working properly */
-        public static final int LINK_PROBLEM_NONE = 0;
-        /** This link has an unknown issue */
-        public static final int LINK_PROBLEM_UNKNOWN = 1;
-    }
-
-    /**
-     * Debug logging
-     */
-    protected static void log(String s) {
-        Log.d(TAG, s);
-    }
-}
diff --git a/core/java/android/net/LinkSocketNotifier.java b/core/java/android/net/LinkSocketNotifier.java
deleted file mode 100644
index e2429d8..0000000
--- a/core/java/android/net/LinkSocketNotifier.java
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Copyright (C) 2010 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.net;
-
-/**
- * Interface used to get feedback about a {@link android.net.LinkSocket}.  Instance is optionally
- * passed when a LinkSocket is constructed.  Multiple LinkSockets may use the same notifier.
- * @hide
- */
-public interface LinkSocketNotifier {
-    /**
-     * This callback function will be called if a better link
-     * becomes available.
-     * TODO - this shouldn't be checked for all cases - what's the conditional
-     *        flag?
-     * If the duplicate socket is accepted, the original will be marked invalid
-     * and additional use will throw exceptions.
-     * @param original the original LinkSocket
-     * @param duplicate the new LinkSocket that better meets the application
-     *                  requirements
-     * @return {@code true} if the application intends to use this link
-     *
-     * REM
-     * TODO - how agressive should we be?
-     * At a minimum CS tracks which LS have this turned on and tracks the requirements
-     * When a new link becomes available, automatically check if any of the LinkSockets
-     *   will care.
-     * If found, grab a refcount on the link so it doesn't go away and send notification
-     * Optionally, periodically setup connection on available networks to check for better links
-     * Maybe pass this info into the LinkFactories so condition changes can be acted on more quickly
-     */
-    public boolean onBetterLinkAvailable(LinkSocket original, LinkSocket duplicate);
-
-    /**
-     * This callback function will be called when a LinkSocket no longer has
-     * an active link.
-     * @param socket the LinkSocket that lost its link
-     *
-     * REM
-     * NetworkStateTracker tells us it is disconnected
-     * CS checks the table for LS on that link
-     * CS calls each callback (need to work out p2p cross process callback)
-     */
-    public void onLinkLost(LinkSocket socket);
-
-    /**
-     * This callback function will be called when an application calls
-     * requestNewLink on a LinkSocket but the LinkSocket is unable to find
-     * a suitable new link.
-     * @param socket the LinkSocket for which a new link was not found
-     * TODO - why the diff between initial request (sync) and requestNewLink?
-     *
-     * REM
-     * CS process of trying to find a new link must track the LS that started it
-     * on failure, call callback
-     */
-    public void onNewLinkUnavailable(LinkSocket socket);
-
-    /**
-     * This callback function will be called when any of the notification-marked
-     * capabilities of the LinkSocket (e.g. upstream bandwidth) have changed.
-     * @param socket the linkSocet for which capabilities have changed
-     * @param changedCapabilities the set of capabilities that the application
-     *                            is interested in and have changed (with new values)
-     *
-     * REM
-     * Maybe pass the interesting capabilities into the Links.
-     * Get notified of every capability change
-     * check for LinkSockets on that Link that are interested in that Capability - call them
-     */
-    public void onCapabilitiesChanged(LinkSocket socket, LinkCapabilities changedCapabilities);
-}
diff --git a/core/java/android/net/MobileDataStateTracker.java b/core/java/android/net/MobileDataStateTracker.java
index 30b61c5..535bbe24 100644
--- a/core/java/android/net/MobileDataStateTracker.java
+++ b/core/java/android/net/MobileDataStateTracker.java
@@ -66,7 +66,6 @@
     private Handler mTarget;
     private Context mContext;
     private LinkProperties mLinkProperties;
-    private LinkCapabilities mLinkCapabilities;
     private boolean mPrivateDnsRouteSet = false;
     private boolean mDefaultRouteSet = false;
 
@@ -200,11 +199,11 @@
         }
         mLinkProperties.setMtu(mContext.getResources().getInteger(
                 com.android.internal.R.integer.config_mobile_mtu));
-        mLinkCapabilities = intent.getParcelableExtra(
-                PhoneConstants.DATA_LINK_CAPABILITIES_KEY);
-        if (mLinkCapabilities == null) {
-            loge("CONNECTED event did not supply link capabilities.");
-            mLinkCapabilities = new LinkCapabilities();
+        mNetworkCapabilities = intent.getParcelableExtra(
+                PhoneConstants.DATA_NETWORK_CAPABILITIES_KEY);
+        if (mNetworkCapabilities == null) {
+            loge("CONNECTED event did not supply network capabilities.");
+            mNetworkCapabilities = new NetworkCapabilities();
         }
     }
 
@@ -316,10 +315,10 @@
                             Slog.d(TAG, "LinkProperties = " );
                         }
 
-                        if (mLinkCapabilities != null) {
-                            Slog.d(TAG, "LinkCapabilities = " + mLinkCapabilities);
+                        if (mNetworkCapabilities != null) {
+                            Slog.d(TAG, mNetworkCapabilities.toString());
                         } else {
-                            Slog.d(TAG, "LinkCapabilities = " );
+                            Slog.d(TAG, "NetworkCapabilities = " );
                         }
                     }
 
@@ -750,14 +749,6 @@
         return new LinkProperties(mLinkProperties);
     }
 
-    /**
-     * @see android.net.NetworkStateTracker#getLinkCapabilities()
-     */
-    @Override
-    public LinkCapabilities getLinkCapabilities() {
-        return new LinkCapabilities(mLinkCapabilities);
-    }
-
     public void supplyMessenger(Messenger messenger) {
         if (VDBG) log(mApnType + " got supplyMessenger");
         AsyncChannel ac = new AsyncChannel();
diff --git a/core/java/android/net/LinkCapabilities.aidl b/core/java/android/net/Network.aidl
similarity index 87%
rename from core/java/android/net/LinkCapabilities.aidl
rename to core/java/android/net/Network.aidl
index df72599..73ba1af 100644
--- a/core/java/android/net/LinkCapabilities.aidl
+++ b/core/java/android/net/Network.aidl
@@ -1,6 +1,6 @@
 /*
 **
-** Copyright (C) 2010 The Android Open Source Project
+** 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.
@@ -17,5 +17,4 @@
 
 package android.net;
 
-parcelable LinkCapabilities;
-
+parcelable Network;
diff --git a/core/java/android/net/Network.java b/core/java/android/net/Network.java
new file mode 100644
index 0000000..f82bc22
--- /dev/null
+++ b/core/java/android/net/Network.java
@@ -0,0 +1,59 @@
+/*
+ * 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.net;
+
+import android.os.Parcelable;
+import android.os.Parcel;
+
+
+/**
+ * Identifies the Network.
+ * @hide
+ */
+public class Network implements Parcelable {
+
+    public final int netId;
+
+    public Network(int netId) {
+        this.netId = netId;
+    }
+
+    public Network(Network that) {
+        this.netId = that.netId;
+    }
+
+    // implement the Parcelable interface
+    public int describeContents() {
+        return 0;
+    }
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(netId);
+    }
+
+    public static final Creator<Network> CREATOR =
+        new Creator<Network>() {
+            public Network createFromParcel(Parcel in) {
+                int netId = in.readInt();
+
+                return new Network(netId);
+            }
+
+            public Network[] newArray(int size) {
+                return new Network[size];
+            }
+    };
+}
diff --git a/core/java/android/net/LinkCapabilities.aidl b/core/java/android/net/NetworkCapabilities.aidl
similarity index 87%
copy from core/java/android/net/LinkCapabilities.aidl
copy to core/java/android/net/NetworkCapabilities.aidl
index df72599..cd7d71c 100644
--- a/core/java/android/net/LinkCapabilities.aidl
+++ b/core/java/android/net/NetworkCapabilities.aidl
@@ -1,6 +1,6 @@
 /*
 **
-** Copyright (C) 2010 The Android Open Source Project
+** 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.
@@ -17,5 +17,5 @@
 
 package android.net;
 
-parcelable LinkCapabilities;
+parcelable NetworkCapabilities;
 
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
new file mode 100644
index 0000000..b783046
--- /dev/null
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -0,0 +1,328 @@
+/*
+ * 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.net;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Log;
+
+import java.lang.IllegalArgumentException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+/**
+ * A class representing the capabilities of a network
+ * @hide
+ */
+public final class NetworkCapabilities implements Parcelable {
+    private static final String TAG = "NetworkCapabilities";
+    private static final boolean DBG = false;
+
+
+    /**
+     * Represents the network's capabilities.  If any are specified they will be satisfied
+     * by any Network that matches all of them.
+     */
+    private long mNetworkCapabilities = (1 << NET_CAPABILITY_NOT_RESTRICTED);
+
+    /**
+     * Values for NetworkCapabilities.  Roughly matches/extends deprecated
+     * ConnectivityManager TYPE_*
+     */
+    public static final int NET_CAPABILITY_MMS            = 0;
+    public static final int NET_CAPABILITY_SUPL           = 1;
+    public static final int NET_CAPABILITY_DUN            = 2;
+    public static final int NET_CAPABILITY_FOTA           = 3;
+    public static final int NET_CAPABILITY_IMS            = 4;
+    public static final int NET_CAPABILITY_CBS            = 5;
+    public static final int NET_CAPABILITY_WIFI_P2P       = 6;
+    public static final int NET_CAPABILITY_IA             = 7;
+    public static final int NET_CAPABILITY_RCS            = 8;
+    public static final int NET_CAPABILITY_XCAP           = 9;
+    public static final int NET_CAPABILITY_EIMS           = 10;
+    public static final int NET_CAPABILITY_NOT_METERED    = 11;
+    public static final int NET_CAPABILITY_INTERNET       = 12;
+    /** Set by default */
+    public static final int NET_CAPABILITY_NOT_RESTRICTED = 13;
+
+    private static final int MIN_NET_CAPABILITY = NET_CAPABILITY_MMS;
+    private static final int MAX_NET_CAPABILITY = NET_CAPABILITY_NOT_RESTRICTED;
+
+    public void addNetworkCapability(int networkCapability) {
+        if (networkCapability < MIN_NET_CAPABILITY ||
+                networkCapability > MAX_NET_CAPABILITY) {
+            throw new IllegalArgumentException("NetworkCapability out of range");
+        }
+        mNetworkCapabilities |= 1 << networkCapability;
+    }
+    public void removeNetworkCapability(int networkCapability) {
+        if (networkCapability < MIN_NET_CAPABILITY ||
+                networkCapability > MAX_NET_CAPABILITY) {
+            throw new IllegalArgumentException("NetworkCapability out of range");
+        }
+        mNetworkCapabilities &= ~(1 << networkCapability);
+    }
+    public Collection<Integer> getNetworkCapabilities() {
+        return enumerateBits(mNetworkCapabilities);
+    }
+    public boolean hasCapability(int networkCapability) {
+        if (networkCapability < MIN_NET_CAPABILITY ||
+                networkCapability > MAX_NET_CAPABILITY) {
+            return false;
+        }
+        return ((mNetworkCapabilities & (1 << networkCapability)) != 0);
+    }
+
+    private Collection<Integer> enumerateBits(long val) {
+        ArrayList<Integer> result = new ArrayList<Integer>();
+        int resource = 0;
+        while (val > 0) {
+            if ((val & 1) == 1) result.add(resource);
+            val = val >> 1;
+            resource++;
+        }
+        return result;
+    }
+
+    private void combineNetCapabilities(NetworkCapabilities nc) {
+        this.mNetworkCapabilities |= nc.mNetworkCapabilities;
+    }
+
+    private boolean satisfiedByNetCapabilities(NetworkCapabilities nc) {
+        return ((nc.mNetworkCapabilities & this.mNetworkCapabilities) == this.mNetworkCapabilities);
+    }
+
+    private boolean equalsNetCapabilities(NetworkCapabilities nc) {
+        return (nc.mNetworkCapabilities == this.mNetworkCapabilities);
+    }
+
+    /**
+     * Representing the transport type.  Apps should generally not care about transport.  A
+     * request for a fast internet connection could be satisfied by a number of different
+     * transports.  If any are specified here it will be satisfied a Network that matches
+     * any of them.  If a caller doesn't care about the transport it should not specify any.
+     */
+    private long mTransportTypes;
+
+    /**
+     * Values for TransportType
+     */
+    public static final int TRANSPORT_CELLULAR = 0;
+    public static final int TRANSPORT_WIFI = 1;
+    public static final int TRANSPORT_BLUETOOTH = 2;
+    public static final int TRANSPORT_ETHERNET = 3;
+
+    private static final int MIN_TRANSPORT = TRANSPORT_CELLULAR;
+    private static final int MAX_TRANSPORT = TRANSPORT_ETHERNET;
+
+    public void addTransportType(int transportType) {
+        if (transportType < MIN_TRANSPORT || transportType > MAX_TRANSPORT) {
+            throw new IllegalArgumentException("TransportType out of range");
+        }
+        mTransportTypes |= 1 << transportType;
+    }
+    public void removeTransportType(int transportType) {
+        if (transportType < MIN_TRANSPORT || transportType > MAX_TRANSPORT) {
+            throw new IllegalArgumentException("TransportType out of range");
+        }
+        mTransportTypes &= ~(1 << transportType);
+    }
+    public Collection<Integer> getTransportTypes() {
+        return enumerateBits(mTransportTypes);
+    }
+    public boolean hasTransport(int transportType) {
+        if (transportType < MIN_TRANSPORT || transportType > MAX_TRANSPORT) {
+            return false;
+        }
+        return ((mTransportTypes & (1 << transportType)) != 0);
+    }
+
+    private void combineTransportTypes(NetworkCapabilities nc) {
+        this.mTransportTypes |= nc.mTransportTypes;
+    }
+    private boolean satisfiedByTransportTypes(NetworkCapabilities nc) {
+        return ((this.mTransportTypes == 0) ||
+                ((this.mTransportTypes & nc.mTransportTypes) != 0));
+    }
+    private boolean equalsTransportTypes(NetworkCapabilities nc) {
+        return (nc.mTransportTypes == this.mTransportTypes);
+    }
+
+    /**
+     * Passive link bandwidth.  This is a rough guide of the expected peak bandwidth
+     * for the first hop on the given transport.  It is not measured, but may take into account
+     * link parameters (Radio technology, allocated channels, etc).
+     */
+    private int mLinkUpBandwidthKbps;
+    private int mLinkDownBandwidthKbps;
+
+    public void setLinkUpstreamBandwidthKbps(int upKbps) {
+        mLinkUpBandwidthKbps = upKbps;
+    }
+    public int getLinkUpstreamBandwidthKbps() {
+        return mLinkUpBandwidthKbps;
+    }
+    public void setLinkDownstreamBandwidthKbps(int downKbps) {
+        mLinkDownBandwidthKbps = downKbps;
+    }
+    public int getLinkDownstreamBandwidthKbps() {
+        return mLinkDownBandwidthKbps;
+    }
+
+    private void combineLinkBandwidths(NetworkCapabilities nc) {
+        this.mLinkUpBandwidthKbps =
+                Math.max(this.mLinkUpBandwidthKbps, nc.mLinkUpBandwidthKbps);
+        this.mLinkDownBandwidthKbps =
+                Math.max(this.mLinkDownBandwidthKbps, nc.mLinkDownBandwidthKbps);
+    }
+    private boolean satisfiedByLinkBandwidths(NetworkCapabilities nc) {
+        return !(this.mLinkUpBandwidthKbps > nc.mLinkUpBandwidthKbps ||
+                this.mLinkDownBandwidthKbps > nc.mLinkDownBandwidthKbps);
+    }
+    private boolean equalsLinkBandwidths(NetworkCapabilities nc) {
+        return (this.mLinkUpBandwidthKbps == nc.mLinkUpBandwidthKbps &&
+                this.mLinkDownBandwidthKbps == nc.mLinkDownBandwidthKbps);
+    }
+
+    /**
+     * Combine a set of Capabilities to this one.  Useful for coming up with the complete set
+     * {@hide}
+     */
+    public void combineCapabilities(NetworkCapabilities nc) {
+        combineNetCapabilities(nc);
+        combineTransportTypes(nc);
+        combineLinkBandwidths(nc);
+    }
+
+    /**
+     * Check if our requirements are satisfied by the given Capabilities.
+     * {@hide}
+     */
+    public boolean satisfiedByNetworkCapabilities(NetworkCapabilities nc) {
+        return (nc != null &&
+                satisfiedByNetCapabilities(nc) &&
+                satisfiedByTransportTypes(nc) &&
+                satisfiedByLinkBandwidths(nc));
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == null || (obj instanceof NetworkCapabilities == false)) return false;
+        NetworkCapabilities that = (NetworkCapabilities)obj;
+        return (equalsNetCapabilities(that) &&
+                equalsTransportTypes(that) &&
+                equalsLinkBandwidths(that));
+    }
+
+    @Override
+    public int hashCode() {
+        return ((int)(mNetworkCapabilities & 0xFFFFFFFF) +
+                ((int)(mNetworkCapabilities >> 32) * 3) +
+                ((int)(mTransportTypes & 0xFFFFFFFF) * 5) +
+                ((int)(mTransportTypes >> 32) * 7) +
+                (mLinkUpBandwidthKbps * 11) +
+                (mLinkDownBandwidthKbps * 13));
+    }
+
+    public NetworkCapabilities() {
+    }
+
+    public NetworkCapabilities(NetworkCapabilities nc) {
+        if (nc != null) {
+            mNetworkCapabilities = nc.mNetworkCapabilities;
+            mTransportTypes = nc.mTransportTypes;
+            mLinkUpBandwidthKbps = nc.mLinkUpBandwidthKbps;
+            mLinkDownBandwidthKbps = nc.mLinkDownBandwidthKbps;
+        }
+    }
+
+    // Parcelable
+    public int describeContents() {
+        return 0;
+    }
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeLong(mNetworkCapabilities);
+        dest.writeLong(mTransportTypes);
+        dest.writeInt(mLinkUpBandwidthKbps);
+        dest.writeInt(mLinkDownBandwidthKbps);
+    }
+    public static final Creator<NetworkCapabilities> CREATOR =
+        new Creator<NetworkCapabilities>() {
+            public NetworkCapabilities createFromParcel(Parcel in) {
+                NetworkCapabilities netCap = new NetworkCapabilities();
+
+                netCap.mNetworkCapabilities = in.readLong();
+                netCap.mTransportTypes = in.readLong();
+                netCap.mLinkUpBandwidthKbps = in.readInt();
+                netCap.mLinkDownBandwidthKbps = in.readInt();
+                return netCap;
+            }
+            public NetworkCapabilities[] newArray(int size) {
+                return new NetworkCapabilities[size];
+            }
+        };
+
+    public String toString() {
+        Collection<Integer> types = getTransportTypes();
+        String transports = (types.size() > 0 ? " Transports: " : "");
+        Iterator<Integer> i = types.iterator();
+        while (i.hasNext()) {
+            switch (i.next()) {
+                case TRANSPORT_CELLULAR:    transports += "CELLULAR"; break;
+                case TRANSPORT_WIFI:        transports += "WIFI"; break;
+                case TRANSPORT_BLUETOOTH:   transports += "BLUETOOTH"; break;
+                case TRANSPORT_ETHERNET:    transports += "ETHERNET"; break;
+            }
+            if (i.hasNext()) transports += "|";
+        }
+
+        types = getNetworkCapabilities();
+        String capabilities = (types.size() > 0 ? " Capabilities: " : "");
+        i = types.iterator();
+        while (i.hasNext()) {
+            switch (i.next().intValue()) {
+                case NET_CAPABILITY_MMS:            capabilities += "MMS"; break;
+                case NET_CAPABILITY_SUPL:           capabilities += "SUPL"; break;
+                case NET_CAPABILITY_DUN:            capabilities += "DUN"; break;
+                case NET_CAPABILITY_FOTA:           capabilities += "FOTA"; break;
+                case NET_CAPABILITY_IMS:            capabilities += "IMS"; break;
+                case NET_CAPABILITY_CBS:            capabilities += "CBS"; break;
+                case NET_CAPABILITY_WIFI_P2P:       capabilities += "WIFI_P2P"; break;
+                case NET_CAPABILITY_IA:             capabilities += "IA"; break;
+                case NET_CAPABILITY_RCS:            capabilities += "RCS"; break;
+                case NET_CAPABILITY_XCAP:           capabilities += "XCAP"; break;
+                case NET_CAPABILITY_EIMS:           capabilities += "EIMS"; break;
+                case NET_CAPABILITY_NOT_METERED:    capabilities += "NOT_METERED"; break;
+                case NET_CAPABILITY_INTERNET:       capabilities += "INTERNET"; break;
+                case NET_CAPABILITY_NOT_RESTRICTED: capabilities += "NOT_RESTRICTED"; break;
+            }
+            if (i.hasNext()) capabilities += "&";
+        }
+
+        String upBand = ((mLinkUpBandwidthKbps > 0) ? " LinkUpBandwidth>=" +
+                mLinkUpBandwidthKbps + "Kbps" : "");
+        String dnBand = ((mLinkDownBandwidthKbps > 0) ? " LinkDnBandwidth>=" +
+                mLinkDownBandwidthKbps + "Kbps" : "");
+
+        return "NetworkCapabilities: [" + transports + capabilities + upBand + dnBand + "]";
+    }
+}
diff --git a/core/java/android/net/NetworkRequest.aidl b/core/java/android/net/NetworkRequest.aidl
new file mode 100644
index 0000000..508defc
--- /dev/null
+++ b/core/java/android/net/NetworkRequest.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.net;
+
+parcelable NetworkRequest;
+
diff --git a/core/java/android/net/NetworkRequest.java b/core/java/android/net/NetworkRequest.java
new file mode 100644
index 0000000..7e3a06d
--- /dev/null
+++ b/core/java/android/net/NetworkRequest.java
@@ -0,0 +1,87 @@
+/*
+ * 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.net;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * @hide
+ */
+public class NetworkRequest implements Parcelable {
+    public final NetworkCapabilities networkCapabilities;
+    public final int requestId;
+    public final boolean legacy;
+    private static final AtomicInteger sRequestId = new AtomicInteger();
+
+    public NetworkRequest(NetworkCapabilities nc) {
+        this(nc, false, sRequestId.incrementAndGet());
+    }
+
+    public NetworkRequest(NetworkCapabilities nc, boolean legacy) {
+        this(nc, legacy, sRequestId.incrementAndGet());
+    }
+
+    private NetworkRequest(NetworkCapabilities nc, boolean legacy, int rId) {
+        requestId = rId;
+        networkCapabilities = nc;
+        this.legacy = legacy;
+    }
+
+    // implement the Parcelable interface
+    public int describeContents() {
+        return 0;
+    }
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeParcelable(networkCapabilities, flags);
+        dest.writeInt(legacy ? 1 : 0);
+        dest.writeInt(requestId);
+    }
+    public static final Creator<NetworkRequest> CREATOR =
+        new Creator<NetworkRequest>() {
+            public NetworkRequest createFromParcel(Parcel in) {
+                NetworkCapabilities nc = (NetworkCapabilities)in.readParcelable(null);
+                boolean legacy = (in.readInt() == 1);
+                int requestId = in.readInt();
+                return new NetworkRequest(nc, legacy, requestId);
+            }
+            public NetworkRequest[] newArray(int size) {
+                return new NetworkRequest[size];
+            }
+        };
+
+    public String toString() {
+        return "NetworkRequest [ id=" + requestId + ", legacy=" + legacy + ", " +
+                networkCapabilities.toString() + " ]";
+    }
+
+    public boolean equals(Object obj) {
+        if (obj instanceof NetworkRequest == false) return false;
+        NetworkRequest that = (NetworkRequest)obj;
+        return (that.legacy == this.legacy &&
+                that.requestId == this.requestId &&
+                ((that.networkCapabilities == null && this.networkCapabilities == null) ||
+                 (that.networkCapabilities != null &&
+                  that.networkCapabilities.equals(this.networkCapabilities))));
+    }
+
+    public int hashCode() {
+        return requestId + (legacy ? 1013 : 2026) + (networkCapabilities.hashCode() * 1051);
+    }
+}
diff --git a/core/java/android/net/NetworkState.java b/core/java/android/net/NetworkState.java
index fbe1f82..2e0e9e4 100644
--- a/core/java/android/net/NetworkState.java
+++ b/core/java/android/net/NetworkState.java
@@ -28,21 +28,21 @@
 
     public final NetworkInfo networkInfo;
     public final LinkProperties linkProperties;
-    public final LinkCapabilities linkCapabilities;
+    public final NetworkCapabilities networkCapabilities;
     /** Currently only used by testing. */
     public final String subscriberId;
     public final String networkId;
 
     public NetworkState(NetworkInfo networkInfo, LinkProperties linkProperties,
-            LinkCapabilities linkCapabilities) {
-        this(networkInfo, linkProperties, linkCapabilities, null, null);
+            NetworkCapabilities networkCapabilities) {
+        this(networkInfo, linkProperties, networkCapabilities, null, null);
     }
 
     public NetworkState(NetworkInfo networkInfo, LinkProperties linkProperties,
-            LinkCapabilities linkCapabilities, String subscriberId, String networkId) {
+            NetworkCapabilities networkCapabilities, String subscriberId, String networkId) {
         this.networkInfo = networkInfo;
         this.linkProperties = linkProperties;
-        this.linkCapabilities = linkCapabilities;
+        this.networkCapabilities = networkCapabilities;
         this.subscriberId = subscriberId;
         this.networkId = networkId;
     }
@@ -50,7 +50,7 @@
     public NetworkState(Parcel in) {
         networkInfo = in.readParcelable(null);
         linkProperties = in.readParcelable(null);
-        linkCapabilities = in.readParcelable(null);
+        networkCapabilities = in.readParcelable(null);
         subscriberId = in.readString();
         networkId = in.readString();
     }
@@ -64,7 +64,7 @@
     public void writeToParcel(Parcel out, int flags) {
         out.writeParcelable(networkInfo, flags);
         out.writeParcelable(linkProperties, flags);
-        out.writeParcelable(linkCapabilities, flags);
+        out.writeParcelable(networkCapabilities, flags);
         out.writeString(subscriberId);
         out.writeString(networkId);
     }
diff --git a/core/java/android/net/NetworkStateTracker.java b/core/java/android/net/NetworkStateTracker.java
index c49b1d1..35500cc 100644
--- a/core/java/android/net/NetworkStateTracker.java
+++ b/core/java/android/net/NetworkStateTracker.java
@@ -111,12 +111,9 @@
     public LinkProperties getLinkProperties();
 
     /**
-     * A capability is an Integer/String pair, the capabilities
-     * are defined in the class LinkSocket#Key.
-     *
      * @return a copy of this connections capabilities, may be empty but never null.
      */
-    public LinkCapabilities getLinkCapabilities();
+    public NetworkCapabilities getNetworkCapabilities();
 
     /**
      * Get interesting information about this network link
@@ -250,4 +247,14 @@
      */
     public void stopSampling(SamplingDataTracker.SamplingSnapshot s);
 
+    /*
+     * Record the current netId
+     */
+    public void setNetId(int netId);
+
+    /*
+     * ?
+     */
+    public Network getNetwork();
+
 }
diff --git a/core/java/android/net/Proxy.java b/core/java/android/net/Proxy.java
index 8f41e85..6a78c29 100644
--- a/core/java/android/net/Proxy.java
+++ b/core/java/android/net/Proxy.java
@@ -273,19 +273,19 @@
         String host = null;
         String port = null;
         String exclList = null;
-        String pacFileUrl = null;
+        Uri pacFileUrl = Uri.EMPTY;
         if (p != null) {
             host = p.getHost();
             port = Integer.toString(p.getPort());
             exclList = p.getExclusionListAsString();
-            pacFileUrl = p.getPacFileUrl().toString();
+            pacFileUrl = p.getPacFileUrl();
         }
         setHttpProxySystemProperty(host, port, exclList, pacFileUrl);
     }
 
     /** @hide */
     public static final void setHttpProxySystemProperty(String host, String port, String exclList,
-            String pacFileUrl) {
+            Uri pacFileUrl) {
         if (exclList != null) exclList = exclList.replace(",", "|");
         if (false) Log.d(TAG, "setHttpProxySystemProperty :"+host+":"+port+" - "+exclList);
         if (host != null) {
@@ -309,7 +309,7 @@
             System.clearProperty("http.nonProxyHosts");
             System.clearProperty("https.nonProxyHosts");
         }
-        if (!TextUtils.isEmpty(pacFileUrl)) {
+        if (!Uri.EMPTY.equals(pacFileUrl)) {
             ProxySelector.setDefault(new PacProxySelector());
         } else {
             ProxySelector.setDefault(sDefaultProxySelector);
diff --git a/core/java/android/net/ProxyDataTracker.java b/core/java/android/net/ProxyDataTracker.java
index 461e8b8..4973b3d 100644
--- a/core/java/android/net/ProxyDataTracker.java
+++ b/core/java/android/net/ProxyDataTracker.java
@@ -104,7 +104,7 @@
     public ProxyDataTracker() {
         mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_PROXY, 0, NETWORK_TYPE, "");
         mLinkProperties = new LinkProperties();
-        mLinkCapabilities = new LinkCapabilities();
+        mNetworkCapabilities = new NetworkCapabilities();
         mNetworkInfo.setIsAvailable(true);
         try {
           mLinkProperties.addDns(InetAddress.getByName(DNS1));
diff --git a/core/java/android/net/ProxyInfo.java b/core/java/android/net/ProxyInfo.java
index b40941f..991d9da 100644
--- a/core/java/android/net/ProxyInfo.java
+++ b/core/java/android/net/ProxyInfo.java
@@ -44,7 +44,7 @@
     private String mExclusionList;
     private String[] mParsedExclusionList;
 
-    private String mPacFileUrl;
+    private Uri mPacFileUrl;
     /**
      *@hide
      */
@@ -85,7 +85,7 @@
      * at the specified URL.
      */
     public static ProxyInfo buildPacProxy(Uri pacUri) {
-        return new ProxyInfo(pacUri.toString());
+        return new ProxyInfo(pacUri);
     }
 
     /**
@@ -96,6 +96,21 @@
         mHost = host;
         mPort = port;
         setExclusionList(exclList);
+        mPacFileUrl = Uri.EMPTY;
+    }
+
+    /**
+     * Create a ProxyProperties that points at a PAC URL.
+     * @hide
+     */
+    public ProxyInfo(Uri pacFileUrl) {
+        mHost = LOCAL_HOST;
+        mPort = LOCAL_PORT;
+        setExclusionList(LOCAL_EXCL_LIST);
+        if (pacFileUrl == null) {
+            throw new NullPointerException();
+        }
+        mPacFileUrl = pacFileUrl;
     }
 
     /**
@@ -106,17 +121,20 @@
         mHost = LOCAL_HOST;
         mPort = LOCAL_PORT;
         setExclusionList(LOCAL_EXCL_LIST);
-        mPacFileUrl = pacFileUrl;
+        mPacFileUrl = Uri.parse(pacFileUrl);
     }
 
     /**
      * Only used in PacManager after Local Proxy is bound.
      * @hide
      */
-    public ProxyInfo(String pacFileUrl, int localProxyPort) {
+    public ProxyInfo(Uri pacFileUrl, int localProxyPort) {
         mHost = LOCAL_HOST;
         mPort = localProxyPort;
         setExclusionList(LOCAL_EXCL_LIST);
+        if (pacFileUrl == null) {
+            throw new NullPointerException();
+        }
         mPacFileUrl = pacFileUrl;
     }
 
@@ -125,7 +143,7 @@
         mPort = port;
         mExclusionList = exclList;
         mParsedExclusionList = parsedExclList;
-        mPacFileUrl = null;
+        mPacFileUrl = Uri.EMPTY;
     }
 
     // copy constructor instead of clone
@@ -137,6 +155,9 @@
             mHost = source.getHost();
             mPort = source.getPort();
             mPacFileUrl = source.mPacFileUrl;
+            if (mPacFileUrl == null) {
+                mPacFileUrl = Uri.EMPTY;
+            }
             mExclusionList = source.getExclusionListAsString();
             mParsedExclusionList = source.mParsedExclusionList;
         }
@@ -158,10 +179,7 @@
      * no PAC script.
      */
     public Uri getPacFileUrl() {
-        if (TextUtils.isEmpty(mPacFileUrl)) {
-            return null;
-        }
-        return Uri.parse(mPacFileUrl);
+        return mPacFileUrl;
     }
 
     /**
@@ -210,7 +228,7 @@
      * @hide
      */
     public boolean isValid() {
-        if (!TextUtils.isEmpty(mPacFileUrl)) return true;
+        if (!Uri.EMPTY.equals(mPacFileUrl)) return true;
         return Proxy.PROXY_VALID == Proxy.validate(mHost == null ? "" : mHost,
                                                 mPort == 0 ? "" : Integer.toString(mPort),
                                                 mExclusionList == null ? "" : mExclusionList);
@@ -234,7 +252,7 @@
     @Override
     public String toString() {
         StringBuilder sb = new StringBuilder();
-        if (mPacFileUrl != null) {
+        if (!Uri.EMPTY.equals(mPacFileUrl)) {
             sb.append("PAC Script: ");
             sb.append(mPacFileUrl);
         } else if (mHost != null) {
@@ -257,13 +275,15 @@
         ProxyInfo p = (ProxyInfo)o;
         // If PAC URL is present in either then they must be equal.
         // Other parameters will only be for fall back.
-        if (!TextUtils.isEmpty(mPacFileUrl)) {
+        if (!Uri.EMPTY.equals(mPacFileUrl)) {
             return mPacFileUrl.equals(p.getPacFileUrl()) && mPort == p.mPort;
         }
-        if (!TextUtils.isEmpty(p.mPacFileUrl)) {
+        if (!Uri.EMPTY.equals(p.mPacFileUrl)) {
             return false;
         }
-        if (mExclusionList != null && !mExclusionList.equals(p.getExclusionListAsString())) return false;
+        if (mExclusionList != null && !mExclusionList.equals(p.getExclusionListAsString())) {
+            return false;
+        }
         if (mHost != null && p.getHost() != null && mHost.equals(p.getHost()) == false) {
             return false;
         }
@@ -296,9 +316,9 @@
      * @hide
      */
     public void writeToParcel(Parcel dest, int flags) {
-        if (mPacFileUrl != null) {
+        if (!Uri.EMPTY.equals(mPacFileUrl)) {
             dest.writeByte((byte)1);
-            dest.writeString(mPacFileUrl);
+            mPacFileUrl.writeToParcel(dest, 0);
             dest.writeInt(mPort);
             return;
         } else {
@@ -325,7 +345,7 @@
                 String host = null;
                 int port = 0;
                 if (in.readByte() != 0) {
-                    String url = in.readString();
+                    Uri url = Uri.CREATOR.createFromParcel(in);
                     int localPort = in.readInt();
                     return new ProxyInfo(url, localPort);
                 }
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 8b7467f..4857533 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -21,6 +21,7 @@
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.Formatter;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
@@ -30,6 +31,8 @@
 import android.text.format.DateFormat;
 import android.util.Printer;
 import android.util.SparseArray;
+import android.util.SparseBooleanArray;
+import android.util.SparseIntArray;
 import android.util.TimeUtils;
 import com.android.internal.os.BatterySipper;
 import com.android.internal.os.BatteryStatsHelper;
@@ -537,6 +540,7 @@
         public static final byte CMD_START = 4;
         public static final byte CMD_CURRENT_TIME = 5;
         public static final byte CMD_OVERFLOW = 6;
+        public static final byte CMD_RESET = 7;
 
         public byte cmd = CMD_NULL;
         
@@ -620,6 +624,8 @@
         public static final int EVENT_SYNC = 0x0004;
         // Number of event types.
         public static final int EVENT_COUNT = 0x0005;
+        // Mask to extract out only the type part of the event.
+        public static final int EVENT_TYPE_MASK = ~(EVENT_FLAG_START|EVENT_FLAG_FINISH);
 
         public static final int EVENT_PROC_START = EVENT_PROC | EVENT_FLAG_START;
         public static final int EVENT_PROC_FINISH = EVENT_PROC | EVENT_FLAG_FINISH;
@@ -684,7 +690,7 @@
                 dest.writeInt(eventCode);
                 eventTag.writeToParcel(dest, flags);
             }
-            if (cmd == CMD_CURRENT_TIME) {
+            if (cmd == CMD_CURRENT_TIME || cmd == CMD_RESET) {
                 dest.writeLong(currentTime);
             }
         }
@@ -722,7 +728,7 @@
                 eventCode = EVENT_NONE;
                 eventTag = null;
             }
-            if (cmd == CMD_CURRENT_TIME) {
+            if (cmd == CMD_CURRENT_TIME || cmd == CMD_RESET) {
                 currentTime = src.readLong();
             } else {
                 currentTime = 0;
@@ -833,7 +839,59 @@
             return true;
         }
     }
-    
+
+    public final static class HistoryEventTracker {
+        private final HashMap<String, SparseIntArray>[] mActiveEvents
+                = (HashMap<String, SparseIntArray>[]) new HashMap[HistoryItem.EVENT_COUNT];
+
+        public boolean updateState(int code, String name, int uid, int poolIdx) {
+            if ((code&HistoryItem.EVENT_FLAG_START) != 0) {
+                int idx = code&HistoryItem.EVENT_TYPE_MASK;
+                HashMap<String, SparseIntArray> active = mActiveEvents[idx];
+                if (active == null) {
+                    active = new HashMap<String, SparseIntArray>();
+                    mActiveEvents[idx] = active;
+                }
+                SparseIntArray uids = active.get(name);
+                if (uids == null) {
+                    uids = new SparseIntArray();
+                    active.put(name, uids);
+                }
+                if (uids.indexOfKey(uid) >= 0) {
+                    // Already set, nothing to do!
+                    return false;
+                }
+                uids.put(uid, poolIdx);
+            } else if ((code&HistoryItem.EVENT_FLAG_FINISH) != 0) {
+                int idx = code&HistoryItem.EVENT_TYPE_MASK;
+                HashMap<String, SparseIntArray> active = mActiveEvents[idx];
+                if (active == null) {
+                    // not currently active, nothing to do.
+                    return false;
+                }
+                SparseIntArray uids = active.get(name);
+                if (uids == null) {
+                    // not currently active, nothing to do.
+                    return false;
+                }
+                idx = uids.indexOfKey(uid);
+                if (idx < 0) {
+                    // not currently active, nothing to do.
+                    return false;
+                }
+                uids.removeAt(idx);
+                if (uids.size() <= 0) {
+                    active.remove(name);
+                }
+            }
+            return true;
+        }
+
+        public HashMap<String, SparseIntArray> getStateForEvent(int code) {
+            return mActiveEvents[code];
+        }
+    }
+
     public static final class BitDescription {
         public final int mask;
         public final int shift;
@@ -861,7 +919,7 @@
             this.shortValues = shortValues;
         }
     }
-    
+
     public abstract int getHistoryTotalSize();
 
     public abstract int getHistoryUsedSize();
@@ -2958,10 +3016,14 @@
                     pw.print(":");
                 }
                 pw.println("START");
-            } else if (rec.cmd == HistoryItem.CMD_CURRENT_TIME) {
+            } else if (rec.cmd == HistoryItem.CMD_CURRENT_TIME
+                    || rec.cmd == HistoryItem.CMD_RESET) {
                 if (checkin) {
                     pw.print(":");
                 }
+                if (rec.cmd == HistoryItem.CMD_RESET) {
+                    pw.print("RESET:");
+                }
                 pw.print("TIME:");
                 if (checkin) {
                     pw.println(rec.currentTime);
@@ -3187,6 +3249,86 @@
     public static final int DUMP_INCLUDE_HISTORY = 1<<3;
     public static final int DUMP_VERBOSE = 1<<4;
 
+    private void dumpHistoryLocked(PrintWriter pw, int flags, long histStart, boolean checkin) {
+        final HistoryPrinter hprinter = new HistoryPrinter();
+        final HistoryItem rec = new HistoryItem();
+        long lastTime = -1;
+        long baseTime = -1;
+        boolean printed = false;
+        HistoryEventTracker tracker = null;
+        while (getNextHistoryLocked(rec)) {
+            lastTime = rec.time;
+            if (baseTime < 0) {
+                baseTime = lastTime;
+            }
+            if (rec.time >= histStart) {
+                if (histStart >= 0 && !printed) {
+                    if (rec.cmd == HistoryItem.CMD_CURRENT_TIME
+                            || rec.cmd == HistoryItem.CMD_RESET) {
+                        printed = true;
+                    } else if (rec.currentTime != 0) {
+                        printed = true;
+                        byte cmd = rec.cmd;
+                        rec.cmd = HistoryItem.CMD_CURRENT_TIME;
+                        if (checkin) {
+                            pw.print(BATTERY_STATS_CHECKIN_VERSION); pw.print(',');
+                            pw.print(HISTORY_DATA); pw.print(',');
+                        }
+                        hprinter.printNextItem(pw, rec, baseTime, checkin,
+                                (flags&DUMP_VERBOSE) != 0);
+                        rec.cmd = cmd;
+                    }
+                    if (tracker != null) {
+                        int oldCode = rec.eventCode;
+                        HistoryTag oldTag = rec.eventTag;
+                        rec.eventTag = new HistoryTag();
+                        for (int i=0; i<HistoryItem.EVENT_COUNT; i++) {
+                            HashMap<String, SparseIntArray> active
+                                    = tracker.getStateForEvent(i);
+                            if (active == null) {
+                                continue;
+                            }
+                            for (HashMap.Entry<String, SparseIntArray> ent
+                                    : active.entrySet()) {
+                                SparseIntArray uids = ent.getValue();
+                                for (int j=0; j<uids.size(); j++) {
+                                    rec.eventCode = i;
+                                    rec.eventTag.string = ent.getKey();
+                                    rec.eventTag.uid = uids.keyAt(j);
+                                    rec.eventTag.poolIdx = uids.valueAt(j);
+                                    if (checkin) {
+                                        pw.print(BATTERY_STATS_CHECKIN_VERSION); pw.print(',');
+                                        pw.print(HISTORY_DATA); pw.print(',');
+                                    }
+                                    hprinter.printNextItem(pw, rec, baseTime, checkin,
+                                            (flags&DUMP_VERBOSE) != 0);
+                                }
+                            }
+                        }
+                        rec.eventCode = oldCode;
+                        rec.eventTag = oldTag;
+                        tracker = null;
+                    }
+                }
+                if (checkin) {
+                    pw.print(BATTERY_STATS_CHECKIN_VERSION); pw.print(',');
+                    pw.print(HISTORY_DATA); pw.print(',');
+                }
+                hprinter.printNextItem(pw, rec, baseTime, checkin,
+                        (flags&DUMP_VERBOSE) != 0);
+            } else if (rec.eventCode != HistoryItem.EVENT_NONE) {
+                if (tracker == null) {
+                    tracker = new HistoryEventTracker();
+                }
+                tracker.updateState(rec.eventCode, rec.eventTag.string,
+                        rec.eventTag.uid, rec.eventTag.poolIdx);
+            }
+        }
+        if (histStart >= 0) {
+            pw.print(checkin ? "NEXT: " : "  NEXT: "); pw.println(lastTime+1);
+        }
+    }
+
     /**
      * Dumps a human-readable summary of the battery statistics to the given PrintWriter.
      *
@@ -3200,9 +3342,6 @@
                 (flags&(DUMP_HISTORY_ONLY|DUMP_UNPLUGGED_ONLY|DUMP_CHARGED_ONLY)) != 0;
 
         if ((flags&DUMP_HISTORY_ONLY) != 0 || !filtering) {
-            long now = getHistoryBaseTime() + SystemClock.elapsedRealtime();
-
-            final HistoryItem rec = new HistoryItem();
             final long historyTotalSize = getHistoryTotalSize();
             final long historyUsedSize = getHistoryUsedSize();
             if (startIteratingHistoryLocked()) {
@@ -3218,35 +3357,7 @@
                     pw.print(" strings using ");
                     printSizeValue(pw, getHistoryStringPoolBytes());
                     pw.println("):");
-                    HistoryPrinter hprinter = new HistoryPrinter();
-                    long lastTime = -1;
-                    long baseTime = -1;
-                    boolean printed = false;
-                    while (getNextHistoryLocked(rec)) {
-                        lastTime = rec.time;
-                        if (baseTime < 0) {
-                            baseTime = lastTime;
-                        }
-                        if (rec.time >= histStart) {
-                            if (histStart >= 0 && !printed) {
-                                if (rec.cmd == HistoryItem.CMD_CURRENT_TIME) {
-                                    printed = true;
-                                } else if (rec.currentTime != 0) {
-                                    printed = true;
-                                    byte cmd = rec.cmd;
-                                    rec.cmd = HistoryItem.CMD_CURRENT_TIME;
-                                    hprinter.printNextItem(pw, rec, baseTime, false,
-                                            (flags&DUMP_VERBOSE) != 0);
-                                    rec.cmd = cmd;
-                                }
-                            }
-                            hprinter.printNextItem(pw, rec, baseTime, false,
-                                    (flags&DUMP_VERBOSE) != 0);
-                        }
-                    }
-                    if (histStart >= 0) {
-                        pw.print("  NEXT: "); pw.println(lastTime+1);
-                    }
+                    dumpHistoryLocked(pw, flags, histStart, false);
                     pw.println();
                 } finally {
                     finishIteratingHistoryLocked();
@@ -3255,6 +3366,7 @@
 
             if (startIteratingOldHistoryLocked()) {
                 try {
+                    final HistoryItem rec = new HistoryItem();
                     pw.println("Old battery History:");
                     HistoryPrinter hprinter = new HistoryPrinter();
                     long baseTime = -1;
@@ -3348,7 +3460,6 @@
                 (flags&(DUMP_HISTORY_ONLY|DUMP_UNPLUGGED_ONLY|DUMP_CHARGED_ONLY)) != 0;
 
         if ((flags&DUMP_INCLUDE_HISTORY) != 0 || (flags&DUMP_HISTORY_ONLY) != 0) {
-            final HistoryItem rec = new HistoryItem();
             if (startIteratingHistoryLocked()) {
                 try {
                     for (int i=0; i<getHistoryStringPoolSize(); i++) {
@@ -3365,37 +3476,7 @@
                         pw.print("\"");
                         pw.println();
                     }
-                    HistoryPrinter hprinter = new HistoryPrinter();
-                    long lastTime = -1;
-                    long baseTime = -1;
-                    boolean printed = false;
-                    while (getNextHistoryLocked(rec)) {
-                        lastTime = rec.time;
-                        if (baseTime < 0) {
-                            baseTime = lastTime;
-                        }
-                        if (rec.time >= histStart) {
-                            if (histStart >= 0 && !printed) {
-                                if (rec.cmd == HistoryItem.CMD_CURRENT_TIME) {
-                                    printed = true;
-                                } else if (rec.currentTime != 0) {
-                                    printed = true;
-                                    byte cmd = rec.cmd;
-                                    rec.cmd = HistoryItem.CMD_CURRENT_TIME;
-                                    pw.print(BATTERY_STATS_CHECKIN_VERSION); pw.print(',');
-                                    pw.print(HISTORY_DATA); pw.print(',');
-                                    hprinter.printNextItem(pw, rec, baseTime, true, false);
-                                    rec.cmd = cmd;
-                                }
-                            }
-                            pw.print(BATTERY_STATS_CHECKIN_VERSION); pw.print(',');
-                            pw.print(HISTORY_DATA); pw.print(',');
-                            hprinter.printNextItem(pw, rec, baseTime, true, false);
-                        }
-                    }
-                    if (histStart >= 0) {
-                        pw.print("NEXT: "); pw.println(lastTime+1);
-                    }
+                    dumpHistoryLocked(pw, flags, histStart, true);
                 } finally {
                     finishIteratingHistoryLocked();
                 }
diff --git a/core/java/android/os/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl
index f5ff185..9e03f95 100644
--- a/core/java/android/os/INetworkManagementService.aidl
+++ b/core/java/android/os/INetworkManagementService.aidl
@@ -455,4 +455,14 @@
      * Check whether the mobile radio is currently active.
      */
     boolean isNetworkActive();
+
+    /**
+     * setup a new network
+     */
+    void createNetwork(int netId, String iface);
+
+    /**
+     * remove a network
+     */
+    void removeNetwork(int netId);
 }
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 1847b55..e1fd46e 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -3838,22 +3838,11 @@
 
         /**
          * Setting that specifies whether display color inversion is enabled.
-         *
-         * @hide
          */
         public static final String ACCESSIBILITY_DISPLAY_INVERSION_ENABLED =
                 "accessibility_display_inversion_enabled";
 
         /**
-         * Integer property that specifies the type of color inversion to
-         * perform. Valid values are defined in AccessibilityManager.
-         *
-         * @hide
-         */
-        public static final String ACCESSIBILITY_DISPLAY_INVERSION =
-                "accessibility_display_inversion";
-
-        /**
          * Setting that specifies whether the quick setting tile for display
          * color space adjustment is enabled.
          *
@@ -3881,44 +3870,6 @@
                 "accessibility_display_daltonizer";
 
         /**
-         * Setting that specifies whether the quick setting tile for display
-         * contrast enhancement is enabled.
-         *
-         * @hide
-         */
-        public static final String ACCESSIBILITY_DISPLAY_CONTRAST_QUICK_SETTING_ENABLED =
-                "accessibility_display_contrast_quick_setting_enabled";
-
-        /**
-         * Setting that specifies whether display contrast enhancement is
-         * enabled.
-         *
-         * @hide
-         */
-        public static final String ACCESSIBILITY_DISPLAY_CONTRAST_ENABLED =
-                "accessibility_display_contrast_enabled";
-
-        /**
-         * Floating point property that specifies display contrast adjustment.
-         * Valid range is [0, ...] where 0 is gray, 1 is normal, and higher
-         * values indicate enhanced contrast.
-         *
-         * @hide
-         */
-        public static final String ACCESSIBILITY_DISPLAY_CONTRAST =
-                "accessibility_display_contrast";
-
-        /**
-         * Floating point property that specifies display brightness adjustment.
-         * Valid range is [-1, 1] where -1 is black, 0 is default, and 1 is
-         * white.
-         *
-         * @hide
-         */
-        public static final String ACCESSIBILITY_DISPLAY_BRIGHTNESS =
-                "accessibility_display_brightness";
-
-        /**
          * The timout for considering a press to be a long press in milliseconds.
          * @hide
          */
diff --git a/core/java/android/service/notification/INotificationListener.aidl b/core/java/android/service/notification/INotificationListener.aidl
index d4b29d8..d4919eb 100644
--- a/core/java/android/service/notification/INotificationListener.aidl
+++ b/core/java/android/service/notification/INotificationListener.aidl
@@ -17,11 +17,15 @@
 package android.service.notification;
 
 import android.service.notification.StatusBarNotification;
+import android.service.notification.NotificationOrderUpdate;
 
 /** @hide */
 oneway interface INotificationListener
 {
-    void onListenerConnected(in String[] notificationKeys);
-    void onNotificationPosted(in StatusBarNotification notification);
-    void onNotificationRemoved(in StatusBarNotification notification);
+    void onListenerConnected(in NotificationOrderUpdate update);
+    void onNotificationPosted(in StatusBarNotification notification,
+            in NotificationOrderUpdate update);
+    void onNotificationRemoved(in StatusBarNotification notification,
+            in NotificationOrderUpdate update);
+    void onNotificationOrderUpdate(in NotificationOrderUpdate update);
 }
\ No newline at end of file
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index 3673f03..a94f45a 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -22,10 +22,13 @@
 import android.content.Context;
 import android.content.Intent;
 import android.os.IBinder;
+import android.os.RemoteException;
 import android.os.ServiceManager;
-import android.os.UserHandle;
 import android.util.Log;
 
+import java.util.Comparator;
+import java.util.HashMap;
+
 /**
  * A service that receives calls from the system when new notifications are posted or removed.
  * <p>To extend this class, you must declare the service in your manifest file with
@@ -46,6 +49,7 @@
             + "[" + getClass().getSimpleName() + "]";
 
     private INotificationListenerWrapper mWrapper = null;
+    private String[] mNotificationKeys;
 
     private INotificationManager mNoMan;
 
@@ -95,6 +99,15 @@
         // optional
     }
 
+    /**
+     * Implement this method to be notified when the notification order cahnges.
+     *
+     * Call {@link #getOrderedNotificationKeys()} to retrieve the new order.
+     */
+    public void onNotificationOrderUpdate() {
+        // optional
+    }
+
     private final INotificationManager getNotificationInterface() {
         if (mNoMan == null) {
             mNoMan = INotificationManager.Stub.asInterface(
@@ -202,7 +215,7 @@
      * Request the list of outstanding notifications (that is, those that are visible to the
      * current user). Useful when you don't know what's already been posted.
      *
-     * @return An array of active notifications.
+     * @return An array of active notifications, sorted in natural order.
      */
     public StatusBarNotification[] getActiveNotifications() {
         return getActiveNotifications(null /*all*/);
@@ -213,7 +226,8 @@
      * current user). Useful when you don't know what's already been posted.
      *
      * @param keys A specific list of notification keys, or {@code null} for all.
-     * @return An array of active notifications.
+     * @return An array of active notifications, sorted in natural order
+     *   if {@code keys} is {@code null}.
      */
     public StatusBarNotification[] getActiveNotifications(String[] keys) {
         if (!isBound()) return null;
@@ -226,21 +240,15 @@
     }
 
     /**
-     * Request the list of outstanding notification keys(that is, those that are visible to the
-     * current user).  You can use the notification keys for subsequent retrieval via
+     * Request the list of notification keys in their current natural order.
+     * You can use the notification keys for subsequent retrieval via
      * {@link #getActiveNotifications(String[]) or dismissal via
      * {@link #cancelNotifications(String[]).
      *
-     * @return An array of active notification keys.
+     * @return An array of active notification keys, in their natural order.
      */
-    public String[] getActiveNotificationKeys() {
-        if (!isBound()) return null;
-        try {
-            return getNotificationInterface().getActiveNotificationKeysFromListener(mWrapper);
-        } catch (android.os.RemoteException ex) {
-            Log.v(TAG, "Unable to contact notification manager", ex);
-        }
-        return null;
+    public String[] getOrderedNotificationKeys() {
+        return mNotificationKeys;
     }
 
     @Override
@@ -261,28 +269,60 @@
 
     private class INotificationListenerWrapper extends INotificationListener.Stub {
         @Override
-        public void onNotificationPosted(StatusBarNotification sbn) {
+        public void onNotificationPosted(StatusBarNotification sbn,
+                NotificationOrderUpdate update) {
             try {
-                NotificationListenerService.this.onNotificationPosted(sbn);
+                // protect subclass from concurrent modifications of (@link mNotificationKeys}.
+                synchronized (mWrapper) {
+                    updateNotificationKeys(update);
+                    NotificationListenerService.this.onNotificationPosted(sbn);
+                }
             } catch (Throwable t) {
-                Log.w(TAG, "Error running onNotificationPosted", t);
+                Log.w(TAG, "Error running onOrderedNotificationPosted", t);
             }
         }
         @Override
-        public void onNotificationRemoved(StatusBarNotification sbn) {
+        public void onNotificationRemoved(StatusBarNotification sbn,
+                NotificationOrderUpdate update) {
             try {
-                NotificationListenerService.this.onNotificationRemoved(sbn);
+                // protect subclass from concurrent modifications of (@link mNotificationKeys}.
+                synchronized (mWrapper) {
+                    updateNotificationKeys(update);
+                    NotificationListenerService.this.onNotificationRemoved(sbn);
+                }
             } catch (Throwable t) {
                 Log.w(TAG, "Error running onNotificationRemoved", t);
             }
         }
         @Override
-        public void onListenerConnected(String[] notificationKeys) {
+        public void onListenerConnected(NotificationOrderUpdate update) {
             try {
-                NotificationListenerService.this.onListenerConnected(notificationKeys);
+                // protect subclass from concurrent modifications of (@link mNotificationKeys}.
+                synchronized (mWrapper) {
+                    updateNotificationKeys(update);
+                    NotificationListenerService.this.onListenerConnected(mNotificationKeys);
+                }
             } catch (Throwable t) {
                 Log.w(TAG, "Error running onListenerConnected", t);
             }
         }
+        @Override
+        public void onNotificationOrderUpdate(NotificationOrderUpdate update)
+                throws RemoteException {
+            try {
+                // protect subclass from concurrent modifications of (@link mNotificationKeys}.
+                synchronized (mWrapper) {
+                    updateNotificationKeys(update);
+                    NotificationListenerService.this.onNotificationOrderUpdate();
+                }
+            } catch (Throwable t) {
+                Log.w(TAG, "Error running onNotificationOrderUpdate", t);
+            }
+        }
+    }
+
+    private void updateNotificationKeys(NotificationOrderUpdate update) {
+        // TODO: avoid garbage by comparing the lists
+        mNotificationKeys = update.getOrderedKeys();
     }
 }
diff --git a/core/java/android/service/notification/NotificationOrderUpdate.aidl b/core/java/android/service/notification/NotificationOrderUpdate.aidl
new file mode 100644
index 0000000..5d50641
--- /dev/null
+++ b/core/java/android/service/notification/NotificationOrderUpdate.aidl
@@ -0,0 +1,19 @@
+/**
+ * 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.service.notification;
+
+parcelable NotificationOrderUpdate;
diff --git a/core/java/android/service/notification/NotificationOrderUpdate.java b/core/java/android/service/notification/NotificationOrderUpdate.java
new file mode 100644
index 0000000..20e19a3
--- /dev/null
+++ b/core/java/android/service/notification/NotificationOrderUpdate.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.
+ */
+package android.service.notification;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+public class NotificationOrderUpdate implements Parcelable {
+    // TODO replace this with an update instead of the whole array
+    private final String[] mKeys;
+
+    /** @hide */
+    public NotificationOrderUpdate(String[] keys) {
+        this.mKeys = keys;
+    }
+
+    public NotificationOrderUpdate(Parcel in) {
+        this.mKeys = in.readStringArray();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeStringArray(this.mKeys);
+    }
+
+    public static final Parcelable.Creator<NotificationOrderUpdate> CREATOR
+            = new Parcelable.Creator<NotificationOrderUpdate>() {
+        public NotificationOrderUpdate createFromParcel(Parcel parcel) {
+            return new NotificationOrderUpdate(parcel);
+        }
+
+        public NotificationOrderUpdate[] newArray(int size) {
+            return new NotificationOrderUpdate[size];
+        }
+    };
+
+    /**
+     * @hide
+     * @return ordered list of keys
+     */
+    String[] getOrderedKeys() {
+        return mKeys;
+    }
+}
diff --git a/core/java/android/speech/tts/TtsEngines.java b/core/java/android/speech/tts/TtsEngines.java
index 4f996cd..9b929a3 100644
--- a/core/java/android/speech/tts/TtsEngines.java
+++ b/core/java/android/speech/tts/TtsEngines.java
@@ -307,6 +307,24 @@
     }
 
     /**
+     * True if a given TTS engine uses the default phone locale as a default locale. Attempts to
+     * read the value from {@link Settings.Secure#TTS_DEFAULT_LOCALE}, failing which the
+     * old style value from {@link Settings.Secure#TTS_DEFAULT_LANG} is read. If
+     * both these values are empty, this methods returns true.
+     *
+     * @param engineName the engine to return the locale for.
+     */
+    public boolean isLocaleSetToDefaultForEngine(String engineName) {
+        return (TextUtils.isEmpty(parseEnginePrefFromList(
+                    getString(mContext.getContentResolver(), Settings.Secure.TTS_DEFAULT_LOCALE),
+                    engineName)) &&
+                    TextUtils.isEmpty(
+                        Settings.Secure.getString(mContext.getContentResolver(),
+                        Settings.Secure.TTS_DEFAULT_LANG)));
+    }
+
+
+    /**
      * Parses a locale preference value delimited by {@link #LOCALE_DELIMITER}.
      * Varies from {@link String#split} in that it will always return an array
      * of length 3 with non null values.
diff --git a/core/java/android/tv/ITvInputClient.aidl b/core/java/android/tv/ITvInputClient.aidl
index 538f8a1..ac83356 100644
--- a/core/java/android/tv/ITvInputClient.aidl
+++ b/core/java/android/tv/ITvInputClient.aidl
@@ -26,6 +26,7 @@
  * @hide
  */
 oneway interface ITvInputClient {
-    void onSessionCreated(in ComponentName name, IBinder token, in InputChannel channel, int seq);
-    void onAvailabilityChanged(in ComponentName name, boolean isAvailable);
+    void onSessionCreated(in String inputId, IBinder token, in InputChannel channel, int seq);
+    void onAvailabilityChanged(in String inputId, boolean isAvailable);
+    void onSessionReleased(int seq);
 }
diff --git a/core/java/android/tv/ITvInputManager.aidl b/core/java/android/tv/ITvInputManager.aidl
index a4c99e4..b756aba 100644
--- a/core/java/android/tv/ITvInputManager.aidl
+++ b/core/java/android/tv/ITvInputManager.aidl
@@ -30,12 +30,12 @@
 interface ITvInputManager {
     List<TvInputInfo> getTvInputList(int userId);
 
-    boolean getAvailability(in ITvInputClient client, in ComponentName name, int userId);
+    boolean getAvailability(in ITvInputClient client, in String inputId, int userId);
 
-    void registerCallback(in ITvInputClient client, in ComponentName name, int userId);
-    void unregisterCallback(in ITvInputClient client, in ComponentName name, int userId);
+    void registerCallback(in ITvInputClient client, in String inputId, int userId);
+    void unregisterCallback(in ITvInputClient client, in String inputId, int userId);
 
-    void createSession(in ITvInputClient client, in ComponentName name, int seq, int userId);
+    void createSession(in ITvInputClient client, in String inputId, int seq, int userId);
     void releaseSession(in IBinder sessionToken, int userId);
 
     void setSurface(in IBinder sessionToken, in Surface surface, int userId);
diff --git a/core/java/android/tv/ITvInputServiceCallback.aidl b/core/java/android/tv/ITvInputServiceCallback.aidl
index e535c81..71fc780 100644
--- a/core/java/android/tv/ITvInputServiceCallback.aidl
+++ b/core/java/android/tv/ITvInputServiceCallback.aidl
@@ -24,5 +24,5 @@
  * @hide
  */
 oneway interface ITvInputServiceCallback {
-    void onAvailabilityChanged(in ComponentName name, boolean isAvailable);
+    void onAvailabilityChanged(in String inputId, boolean isAvailable);
 }
diff --git a/core/java/android/tv/TvInputInfo.java b/core/java/android/tv/TvInputInfo.java
index 90e4177..50462cc 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 = new ComponentName(si.packageName, si.name).flattenToShortString();
+        mId = generateInputIdForComponenetName(new ComponentName(si.packageName, si.name));
     }
 
     /**
@@ -75,7 +75,7 @@
      * Loads the user-displayed label for this TV input service.
      *
      * @param pm Supplies a PackageManager used to load the TV input's resources.
-     * @return Returns a CharSequence containing the TV input's label. If the TV input does not have
+     * @return a CharSequence containing the TV input's label. If the TV input does not have
      *         a label, its name is returned.
      */
     public CharSequence loadLabel(PackageManager pm) {
@@ -128,6 +128,17 @@
     }
 
     /**
+     * Used to generate an input id from a ComponentName.
+     *
+     * @param name the component name for generating an input id.
+     * @return the generated input id for the given {@code name}.
+     * @hide
+     */
+    public static final String generateInputIdForComponenetName(ComponentName name) {
+        return name.flattenToShortString();
+    }
+
+    /**
      * Used to make this class parcelable.
      *
      * @hide
diff --git a/core/java/android/tv/TvInputManager.java b/core/java/android/tv/TvInputManager.java
index 7b9b1fb..c5f179a 100644
--- a/core/java/android/tv/TvInputManager.java
+++ b/core/java/android/tv/TvInputManager.java
@@ -16,7 +16,6 @@
 
 package android.tv;
 
-import android.content.ComponentName;
 import android.graphics.Rect;
 import android.net.Uri;
 import android.os.Handler;
@@ -50,15 +49,15 @@
     private final ITvInputManager mService;
 
     // A mapping from an input to the list of its TvInputListenerRecords.
-    private final Map<ComponentName, List<TvInputListenerRecord>> mTvInputListenerRecordsMap =
-            new HashMap<ComponentName, List<TvInputListenerRecord>>();
+    private final Map<String, List<TvInputListenerRecord>> mTvInputListenerRecordsMap =
+            new HashMap<String, List<TvInputListenerRecord>>();
 
-    // A mapping from the sequence number of a session to its SessionCreateCallbackRecord.
-    private final SparseArray<SessionCreateCallbackRecord> mSessionCreateCallbackRecordMap =
-            new SparseArray<SessionCreateCallbackRecord>();
+    // A mapping from the sequence number of a session to its SessionCallbackRecord.
+    private final SparseArray<SessionCallbackRecord> mSessionCallbackRecordMap =
+            new SparseArray<SessionCallbackRecord>();
 
     // A sequence number for the next session to be created. Should be protected by a lock
-    // {@code mSessionCreateCallbackRecordMap}.
+    // {@code mSessionCallbackRecordMap}.
     private int mNextSeq;
 
     private final ITvInputClient mClient;
@@ -68,31 +67,52 @@
     /**
      * Interface used to receive the created session.
      */
-    public interface SessionCreateCallback {
+    public abstract static class SessionCallback {
         /**
          * This is called after {@link TvInputManager#createSession} has been processed.
          *
          * @param session A {@link TvInputManager.Session} instance created. This can be
          *            {@code null} if the creation request failed.
          */
-        void onSessionCreated(Session session);
+        public void onSessionCreated(Session session) {
+        }
+
+        /**
+         * This is called when {@link TvInputManager.Session} is released.
+         * This typically happens when the process hosting the session has crashed or been killed.
+         *
+         * @param session A {@link TvInputManager.Session} instance released.
+         */
+        public void onSessionReleased(Session session) {
+        }
     }
 
-    private static final class SessionCreateCallbackRecord {
-        private final SessionCreateCallback mSessionCreateCallback;
+    private static final class SessionCallbackRecord {
+        private final SessionCallback mSessionCallback;
         private final Handler mHandler;
+        private Session mSession;
 
-        public SessionCreateCallbackRecord(SessionCreateCallback sessionCreateCallback,
+        public SessionCallbackRecord(SessionCallback sessionCallback,
                 Handler handler) {
-            mSessionCreateCallback = sessionCreateCallback;
+            mSessionCallback = sessionCallback;
             mHandler = handler;
         }
 
         public void postSessionCreated(final Session session) {
+            mSession = session;
             mHandler.post(new Runnable() {
                 @Override
                 public void run() {
-                    mSessionCreateCallback.onSessionCreated(session);
+                    mSessionCallback.onSessionCreated(session);
+                }
+            });
+        }
+
+        public void postSessionReleased() {
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    mSessionCallback.onSessionReleased(mSession);
                 }
             });
         }
@@ -105,12 +125,11 @@
         /**
          * This is called when the availability status of a given TV input is changed.
          *
-         * @param name {@link ComponentName} of {@link android.app.Service} that implements the
-         *            given TV input.
+         * @param inputId the id of the TV input.
          * @param isAvailable {@code true} if the given TV input is available to show TV programs.
          *            {@code false} otherwise.
          */
-        public void onAvailabilityChanged(ComponentName name, boolean isAvailable) {
+        public void onAvailabilityChanged(String inputId, boolean isAvailable) {
         }
     }
 
@@ -127,11 +146,11 @@
             return mListener;
         }
 
-        public void postAvailabilityChanged(final ComponentName name, final boolean isAvailable) {
+        public void postAvailabilityChanged(final String inputId, final boolean isAvailable) {
             mHandler.post(new Runnable() {
                 @Override
                 public void run() {
-                    mListener.onAvailabilityChanged(name, isAvailable);
+                    mListener.onAvailabilityChanged(inputId, isAvailable);
                 }
             });
         }
@@ -145,34 +164,48 @@
         mUserId = userId;
         mClient = new ITvInputClient.Stub() {
             @Override
-            public void onSessionCreated(ComponentName name, IBinder token, InputChannel channel,
+            public void onSessionCreated(String inputId, IBinder token, InputChannel channel,
                     int seq) {
-                synchronized (mSessionCreateCallbackRecordMap) {
-                    SessionCreateCallbackRecord record = mSessionCreateCallbackRecordMap.get(seq);
-                    mSessionCreateCallbackRecordMap.delete(seq);
+                synchronized (mSessionCallbackRecordMap) {
+                    SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
                     if (record == null) {
                         Log.e(TAG, "Callback not found for " + token);
                         return;
                     }
                     Session session = null;
                     if (token != null) {
-                        session = new Session(token, channel, mService, mUserId);
+                        session = new Session(token, channel, mService, mUserId, seq,
+                                mSessionCallbackRecordMap);
                     }
                     record.postSessionCreated(session);
                 }
             }
 
             @Override
-            public void onAvailabilityChanged(ComponentName name, boolean isAvailable) {
+            public void onSessionReleased(int seq) {
+                synchronized (mSessionCallbackRecordMap) {
+                    SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
+                    mSessionCallbackRecordMap.delete(seq);
+                    if (record == null) {
+                        Log.e(TAG, "Callback not found for seq:" + seq);
+                        return;
+                    }
+                    record.mSession.releaseInternal();
+                    record.postSessionReleased();
+                }
+            }
+
+            @Override
+            public void onAvailabilityChanged(String inputId, boolean isAvailable) {
                 synchronized (mTvInputListenerRecordsMap) {
-                    List<TvInputListenerRecord> records = mTvInputListenerRecordsMap.get(name);
+                    List<TvInputListenerRecord> records = mTvInputListenerRecordsMap.get(inputId);
                     if (records == null) {
                         // Silently ignore - no listener is registered yet.
                         return;
                     }
                     int recordsCount = records.size();
                     for (int i = 0; i < recordsCount; i++) {
-                        records.get(i).postAvailabilityChanged(name, isAvailable);
+                        records.get(i).postAvailabilityChanged(inputId, isAvailable);
                     }
                 }
             }
@@ -195,24 +228,23 @@
     /**
      * Returns the availability of a given TV input.
      *
-     * @param name {@link ComponentName} of {@link android.app.Service} that implements the given TV
-     *            input.
+     * @param inputId the id of the TV input.
      * @throws IllegalArgumentException if the argument is {@code null}.
      * @throws IllegalStateException If there is no {@link TvInputListener} registered on the given
      *             TV input.
      */
-    public boolean getAvailability(ComponentName name) {
-        if (name == null) {
-            throw new IllegalArgumentException("name cannot be null");
+    public boolean getAvailability(String inputId) {
+        if (inputId == null) {
+            throw new IllegalArgumentException("id cannot be null");
         }
         synchronized (mTvInputListenerRecordsMap) {
-            List<TvInputListenerRecord> records = mTvInputListenerRecordsMap.get(name);
+            List<TvInputListenerRecord> records = mTvInputListenerRecordsMap.get(inputId);
             if (records == null || records.size() == 0) {
                 throw new IllegalStateException("At least one listener should be registered.");
             }
         }
         try {
-            return mService.getAvailability(mClient, name, mUserId);
+            return mService.getAvailability(mClient, inputId, mUserId);
         } catch (RemoteException e) {
             throw new RuntimeException(e);
         }
@@ -221,15 +253,14 @@
     /**
      * Registers a {@link TvInputListener} for a given TV input.
      *
-     * @param name {@link ComponentName} of {@link android.app.Service} that implements the given TV
-     *            input.
+     * @param inputId the id of the TV input.
      * @param listener a listener used to monitor status of the given TV input.
      * @param handler a {@link Handler} that the status change will be delivered to.
      * @throws IllegalArgumentException if any of the arguments is {@code null}.
      */
-    public void registerListener(ComponentName name, TvInputListener listener, Handler handler) {
-        if (name == null) {
-            throw new IllegalArgumentException("name cannot be null");
+    public void registerListener(String inputId, TvInputListener listener, Handler handler) {
+        if (inputId == null) {
+            throw new IllegalArgumentException("id cannot be null");
         }
         if (listener == null) {
             throw new IllegalArgumentException("listener cannot be null");
@@ -238,12 +269,12 @@
             throw new IllegalArgumentException("handler cannot be null");
         }
         synchronized (mTvInputListenerRecordsMap) {
-            List<TvInputListenerRecord> records = mTvInputListenerRecordsMap.get(name);
+            List<TvInputListenerRecord> records = mTvInputListenerRecordsMap.get(inputId);
             if (records == null) {
                 records = new ArrayList<TvInputListenerRecord>();
-                mTvInputListenerRecordsMap.put(name, records);
+                mTvInputListenerRecordsMap.put(inputId, records);
                 try {
-                    mService.registerCallback(mClient, name, mUserId);
+                    mService.registerCallback(mClient, inputId, mUserId);
                 } catch (RemoteException e) {
                     throw new RuntimeException(e);
                 }
@@ -255,22 +286,21 @@
     /**
      * Unregisters the existing {@link TvInputListener} for a given TV input.
      *
-     * @param name {@link ComponentName} of {@link android.app.Service} that implements the given TV
-     *            input.
+     * @param inputId the id of the TV input.
      * @param listener the existing listener to remove for the given TV input.
      * @throws IllegalArgumentException if any of the arguments is {@code null}.
      */
-    public void unregisterListener(ComponentName name, final TvInputListener listener) {
-        if (name == null) {
-            throw new IllegalArgumentException("name cannot be null");
+    public void unregisterListener(String inputId, final TvInputListener listener) {
+        if (inputId == null) {
+            throw new IllegalArgumentException("id cannot be null");
         }
         if (listener == null) {
             throw new IllegalArgumentException("listener cannot be null");
         }
         synchronized (mTvInputListenerRecordsMap) {
-            List<TvInputListenerRecord> records = mTvInputListenerRecordsMap.get(name);
+            List<TvInputListenerRecord> records = mTvInputListenerRecordsMap.get(inputId);
             if (records == null) {
-                Log.e(TAG, "No listener found for " + name.getClassName());
+                Log.e(TAG, "No listener found for " + inputId);
                 return;
             }
             for (Iterator<TvInputListenerRecord> it = records.iterator(); it.hasNext();) {
@@ -281,11 +311,11 @@
             }
             if (records.isEmpty()) {
                 try {
-                    mService.unregisterCallback(mClient, name, mUserId);
+                    mService.unregisterCallback(mClient, inputId, mUserId);
                 } catch (RemoteException e) {
                     throw new RuntimeException(e);
                 } finally {
-                    mTvInputListenerRecordsMap.remove(name);
+                    mTvInputListenerRecordsMap.remove(inputId);
                 }
             }
         }
@@ -298,16 +328,15 @@
      * the given TV input.
      * </p>
      *
-     * @param name {@link ComponentName} of {@link android.app.Service} that implements the given TV
-     *            input.
+     * @param inputId the id of the TV input.
      * @param callback a callback used to receive the created session.
      * @param handler a {@link Handler} that the session creation will be delivered to.
      * @throws IllegalArgumentException if any of the arguments is {@code null}.
      */
-    public void createSession(ComponentName name, final SessionCreateCallback callback,
+    public void createSession(String inputId, final SessionCallback callback,
             Handler handler) {
-        if (name == null) {
-            throw new IllegalArgumentException("name cannot be null");
+        if (inputId == null) {
+            throw new IllegalArgumentException("id cannot be null");
         }
         if (callback == null) {
             throw new IllegalArgumentException("callback cannot be null");
@@ -315,12 +344,12 @@
         if (handler == null) {
             throw new IllegalArgumentException("handler cannot be null");
         }
-        SessionCreateCallbackRecord record = new SessionCreateCallbackRecord(callback, handler);
-        synchronized (mSessionCreateCallbackRecordMap) {
+        SessionCallbackRecord record = new SessionCallbackRecord(callback, handler);
+        synchronized (mSessionCallbackRecordMap) {
             int seq = mNextSeq++;
-            mSessionCreateCallbackRecordMap.put(seq, record);
+            mSessionCallbackRecordMap.put(seq, record);
             try {
-                mService.createSession(mClient, name, seq, mUserId);
+                mService.createSession(mClient, inputId, seq, mUserId);
             } catch (RemoteException e) {
                 throw new RuntimeException(e);
             }
@@ -337,6 +366,7 @@
 
         private final ITvInputManager mService;
         private final int mUserId;
+        private final int mSeq;
 
         // For scheduling input event handling on the main thread. This also serves as a lock to
         // protect pending input events and the input channel.
@@ -344,17 +374,21 @@
 
         private final Pool<PendingEvent> mPendingEventPool = new SimplePool<PendingEvent>(20);
         private final SparseArray<PendingEvent> mPendingEvents = new SparseArray<PendingEvent>(20);
+        private final SparseArray<SessionCallbackRecord> mSessionCallbackRecordMap;
 
         private IBinder mToken;
         private TvInputEventSender mSender;
         private InputChannel mChannel;
 
         /** @hide */
-        private Session(IBinder token, InputChannel channel, ITvInputManager service, int userId) {
+        private Session(IBinder token, InputChannel channel, ITvInputManager service, int userId,
+                int seq, SparseArray<SessionCallbackRecord> sessionCallbackRecordMap) {
             mToken = token;
             mChannel = channel;
             mService = service;
             mUserId = userId;
+            mSeq = seq;
+            mSessionCallbackRecordMap = sessionCallbackRecordMap;
         }
 
         /**
@@ -368,22 +402,11 @@
             }
             try {
                 mService.releaseSession(mToken, mUserId);
-                mToken = null;
             } catch (RemoteException e) {
                 throw new RuntimeException(e);
             }
 
-            synchronized (mHandler) {
-                if (mChannel != null) {
-                    if (mSender != null) {
-                        flushPendingEventsLocked();
-                        mSender.dispose();
-                        mSender = null;
-                    }
-                    mChannel.dispose();
-                    mChannel = null;
-                }
-            }
+            releaseInternal();
         }
 
         /**
@@ -675,6 +698,24 @@
             mPendingEventPool.release(p);
         }
 
+        private void releaseInternal() {
+            mToken = null;
+            synchronized (mHandler) {
+                if (mChannel != null) {
+                    if (mSender != null) {
+                        flushPendingEventsLocked();
+                        mSender.dispose();
+                        mSender = null;
+                    }
+                    mChannel.dispose();
+                    mChannel = null;
+                }
+            }
+            synchronized (mSessionCallbackRecordMap) {
+                mSessionCallbackRecordMap.remove(mSeq);
+            }
+        }
+
         private final class InputEventHandler extends Handler {
             public static final int MSG_SEND_INPUT_EVENT = 1;
             public static final int MSG_TIMEOUT_INPUT_EVENT = 2;
diff --git a/core/java/android/tv/TvInputService.java b/core/java/android/tv/TvInputService.java
index 70e7f95..1d6298d 100644
--- a/core/java/android/tv/TvInputService.java
+++ b/core/java/android/tv/TvInputService.java
@@ -60,7 +60,7 @@
      */
     public static final String SERVICE_INTERFACE = "android.tv.TvInputService";
 
-    private ComponentName mComponentName;
+    private String mId;
     private final Handler mHandler = new ServiceHandler();
     private final RemoteCallbackList<ITvInputServiceCallback> mCallbacks =
             new RemoteCallbackList<ITvInputServiceCallback>();
@@ -69,7 +69,8 @@
     @Override
     public void onCreate() {
         super.onCreate();
-        mComponentName = new ComponentName(getPackageName(), getClass().getName());
+        mId = TvInputInfo.generateInputIdForComponenetName(
+                new ComponentName(getPackageName(), getClass().getName()));
     }
 
     @Override
@@ -82,7 +83,7 @@
                     // The first time a callback is registered, the service needs to report its
                     // availability status so that the system can know its initial value.
                     try {
-                        cb.onAvailabilityChanged(mComponentName, mAvailable);
+                        cb.onAvailabilityChanged(mId, mAvailable);
                     } catch (RemoteException e) {
                         Log.e(TAG, "error in onAvailabilityChanged", e);
                     }
@@ -531,8 +532,7 @@
                     int n = mCallbacks.beginBroadcast();
                     try {
                         for (int i = 0; i < n; i++) {
-                            mCallbacks.getBroadcastItem(i).onAvailabilityChanged(mComponentName,
-                                    isAvailable);
+                            mCallbacks.getBroadcastItem(i).onAvailabilityChanged(mId, isAvailable);
                         }
                     } catch (RemoteException e) {
                         Log.e(TAG, "Unexpected exception", e);
diff --git a/core/java/android/tv/TvView.java b/core/java/android/tv/TvView.java
index 289823b..80501e8 100644
--- a/core/java/android/tv/TvView.java
+++ b/core/java/android/tv/TvView.java
@@ -16,13 +16,13 @@
 
 package android.tv;
 
-import android.content.ComponentName;
 import android.content.Context;
 import android.graphics.Rect;
 import android.os.Handler;
+import android.text.TextUtils;
 import android.tv.TvInputManager.Session;
 import android.tv.TvInputManager.Session.FinishedInputEventCallback;
-import android.tv.TvInputManager.SessionCreateCallback;
+import android.tv.TvInputManager.SessionCallback;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.InputEvent;
@@ -46,7 +46,7 @@
     private boolean mOverlayViewCreated;
     private Rect mOverlayViewFrame;
     private final TvInputManager mTvInputManager;
-    private SessionCreateCallback mSessionCreateCallback;
+    private SessionCallback mSessionCallback;
     private OnUnhandledInputEventListener mOnUnhandledInputEventListener;
 
     private final SurfaceHolder.Callback mSurfaceHolderCallback = new SurfaceHolder.Callback() {
@@ -108,19 +108,19 @@
     }
 
     /**
-     * Binds a TV input to this view. {@link SessionCreateCallback#onSessionCreated} will be
+     * Binds a TV input to this view. {@link SessionCallback#onSessionCreated} will be
      * called to send the result of this binding with {@link TvInputManager.Session}.
      * If a TV input is already bound, the input will be unbound from this view and its session
      * will be released.
      *
-     * @param name TV input name will be bound to this view.
+     * @param inputId the id of TV input which will be bound to this view.
      * @param callback called when TV input is bound. The callback sends
      *        {@link TvInputManager.Session}
      * @throws IllegalArgumentException if any of the arguments is {@code null}.
      */
-    public void bindTvInput(ComponentName name, SessionCreateCallback callback) {
-        if (name == null) {
-            throw new IllegalArgumentException("name cannot be null");
+    public void bindTvInput(String inputId, SessionCallback callback) {
+        if (TextUtils.isEmpty(inputId)) {
+            throw new IllegalArgumentException("inputId cannot be null or an empty string");
         }
         if (callback == null) {
             throw new IllegalArgumentException("callback cannot be null");
@@ -130,11 +130,11 @@
         }
         // When bindTvInput is called multiple times before the callback is called,
         // only the callback of the last bindTvInput call will be actually called back.
-        // The previous callbacks will be ignored. For the logic, mSessionCreateCallback
+        // The previous callbacks will be ignored. For the logic, mSessionCallback
         // is newly assigned for every bindTvInput call and compared with
         // MySessionCreateCallback.this.
-        mSessionCreateCallback = new MySessionCreateCallback(callback);
-        mTvInputManager.createSession(name, mSessionCreateCallback, mHandler);
+        mSessionCallback = new MySessionCallback(callback);
+        mTvInputManager.createSession(inputId, mSessionCallback, mHandler);
     }
 
     /**
@@ -196,7 +196,9 @@
         if (mSession == null) {
             return false;
         }
-        int ret = mSession.dispatchInputEvent(event, event, mFinishedInputEventCallback, mHandler);
+        InputEvent copiedEvent = event.copy();
+        int ret = mSession.dispatchInputEvent(copiedEvent, copiedEvent, mFinishedInputEventCallback,
+                mHandler);
         return ret != Session.DISPATCH_NOT_HANDLED;
     }
 
@@ -209,7 +211,9 @@
         if (mSession == null) {
             return false;
         }
-        int ret = mSession.dispatchInputEvent(event, event, mFinishedInputEventCallback, mHandler);
+        InputEvent copiedEvent = event.copy();
+        int ret = mSession.dispatchInputEvent(copiedEvent, copiedEvent, mFinishedInputEventCallback,
+                mHandler);
         return ret != Session.DISPATCH_NOT_HANDLED;
     }
 
@@ -222,7 +226,9 @@
         if (mSession == null) {
             return false;
         }
-        int ret = mSession.dispatchInputEvent(event, event, mFinishedInputEventCallback, mHandler);
+        InputEvent copiedEvent = event.copy();
+        int ret = mSession.dispatchInputEvent(copiedEvent, copiedEvent, mFinishedInputEventCallback,
+                mHandler);
         return ret != Session.DISPATCH_NOT_HANDLED;
     }
 
@@ -235,7 +241,9 @@
         if (mSession == null) {
             return false;
         }
-        int ret = mSession.dispatchInputEvent(event, event, mFinishedInputEventCallback, mHandler);
+        InputEvent copiedEvent = event.copy();
+        int ret = mSession.dispatchInputEvent(copiedEvent, copiedEvent, mFinishedInputEventCallback,
+                mHandler);
         return ret != Session.DISPATCH_NOT_HANDLED;
     }
 
@@ -328,16 +336,16 @@
         boolean onUnhandledInputEvent(InputEvent event);
     }
 
-    private class MySessionCreateCallback implements SessionCreateCallback {
-        final SessionCreateCallback mExternalCallback;
+    private class MySessionCallback extends SessionCallback {
+        final SessionCallback mExternalCallback;
 
-        MySessionCreateCallback(SessionCreateCallback externalCallback) {
+        MySessionCallback(SessionCallback externalCallback) {
             mExternalCallback = externalCallback;
         }
 
         @Override
         public void onSessionCreated(Session session) {
-            if (this != mSessionCreateCallback) {
+            if (this != mSessionCallback) {
                 // This callback is obsolete.
                 session.release();
                 return;
@@ -356,5 +364,13 @@
                 mExternalCallback.onSessionCreated(session);
             }
         }
+
+        @Override
+        public void onSessionReleased(Session session) {
+            mSession = null;
+            if (mExternalCallback != null) {
+                mExternalCallback.onSessionReleased(session);
+            }
+        }
     }
 }
diff --git a/core/java/android/util/TimeUtils.java b/core/java/android/util/TimeUtils.java
index 33964a0..8f4b710 100644
--- a/core/java/android/util/TimeUtils.java
+++ b/core/java/android/util/TimeUtils.java
@@ -245,6 +245,9 @@
     private static final int SECONDS_PER_HOUR = 60 * 60;
     private static final int SECONDS_PER_DAY = 24 * 60 * 60;
 
+    /** @hide */
+    public static final long NANOS_PER_MS = 1000000;
+
     private static final Object sFormatSync = new Object();
     private static char[] sFormatStr = new char[HUNDRED_DAY_FIELD_LEN+5];
 
diff --git a/core/java/android/view/Choreographer.java b/core/java/android/view/Choreographer.java
index 0a76075..1066430 100644
--- a/core/java/android/view/Choreographer.java
+++ b/core/java/android/view/Choreographer.java
@@ -112,8 +112,6 @@
     private static final int SKIPPED_FRAME_WARNING_LIMIT = SystemProperties.getInt(
             "debug.choreographer.skipwarning", 30);
 
-    private static final long NANOS_PER_MS = 1000000;
-
     private static final int MSG_DO_FRAME = 0;
     private static final int MSG_DO_SCHEDULE_VSYNC = 1;
     private static final int MSG_DO_SCHEDULE_CALLBACK = 2;
@@ -263,7 +261,7 @@
      * @return The refresh rate as the nanoseconds between frames
      * @hide
      */
-    long getFrameIntervalNanos() {
+    public long getFrameIntervalNanos() {
         return mFrameIntervalNanos;
     }
 
@@ -456,7 +454,7 @@
      * @hide
      */
     public long getFrameTime() {
-        return getFrameTimeNanos() / NANOS_PER_MS;
+        return getFrameTimeNanos() / TimeUtils.NANOS_PER_MS;
     }
 
     /**
@@ -497,7 +495,7 @@
                 }
             } else {
                 final long nextFrameTime = Math.max(
-                        mLastFrameTimeNanos / NANOS_PER_MS + sFrameDelay, now);
+                        mLastFrameTimeNanos / TimeUtils.NANOS_PER_MS + sFrameDelay, now);
                 if (DEBUG) {
                     Log.d(TAG, "Scheduling next frame in " + (nextFrameTime - now) + " ms.");
                 }
@@ -746,7 +744,7 @@
             mFrame = frame;
             Message msg = Message.obtain(mHandler, this);
             msg.setAsynchronous(true);
-            mHandler.sendMessageAtTime(msg, timestampNanos / NANOS_PER_MS);
+            mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);
         }
 
         @Override
diff --git a/core/java/android/view/RenderNodeAnimator.java b/core/java/android/view/RenderNodeAnimator.java
index a675821..ec4d560 100644
--- a/core/java/android/view/RenderNodeAnimator.java
+++ b/core/java/android/view/RenderNodeAnimator.java
@@ -16,10 +16,17 @@
 
 package android.view;
 
+import android.animation.TimeInterpolator;
 import android.graphics.Canvas;
 import android.graphics.CanvasProperty;
 import android.graphics.Paint;
 import android.util.SparseIntArray;
+import android.util.TimeUtils;
+
+import com.android.internal.util.VirtualRefBasePtr;
+import com.android.internal.view.animation.FallbackLUTInterpolator;
+import com.android.internal.view.animation.HasNativeInterpolator;
+import com.android.internal.view.animation.NativeInterpolatorFactory;
 
 import java.lang.ref.WeakReference;
 
@@ -69,34 +76,69 @@
     public static final int DELTA_TYPE_ABSOLUTE = 0;
     public static final int DELTA_TYPE_DELTA = 1;
 
+    private VirtualRefBasePtr mNativePtr;
+
     private RenderNode mTarget;
-    private long mNativePtr;
+    private TimeInterpolator mInterpolator;
+    private boolean mStarted = false;
 
     public int mapViewPropertyToRenderProperty(int viewProperty) {
         return sViewPropertyAnimatorMap.get(viewProperty);
     }
 
     public RenderNodeAnimator(int property, int deltaType, float deltaValue) {
-        mNativePtr = nCreateAnimator(new WeakReference<RenderNodeAnimator>(this),
-                property, deltaType, deltaValue);
+        init(nCreateAnimator(new WeakReference<RenderNodeAnimator>(this),
+                property, deltaType, deltaValue));
     }
 
     public RenderNodeAnimator(CanvasProperty<Float> property, int deltaType, float deltaValue) {
-        mNativePtr = nCreateCanvasPropertyFloatAnimator(
+        init(nCreateCanvasPropertyFloatAnimator(
                 new WeakReference<RenderNodeAnimator>(this),
-                property.getNativeContainer(), deltaType, deltaValue);
+                property.getNativeContainer(), deltaType, deltaValue));
     }
 
     public RenderNodeAnimator(CanvasProperty<Paint> property, int paintField,
             int deltaType, float deltaValue) {
-        mNativePtr = nCreateCanvasPropertyPaintAnimator(
+        init(nCreateCanvasPropertyPaintAnimator(
                 new WeakReference<RenderNodeAnimator>(this),
-                property.getNativeContainer(), paintField, deltaType, deltaValue);
+                property.getNativeContainer(), paintField, deltaType, deltaValue));
+    }
+
+    private void init(long ptr) {
+        mNativePtr = new VirtualRefBasePtr(ptr);
+    }
+
+    private void checkMutable() {
+        if (mStarted) {
+            throw new IllegalStateException("Animator has already started, cannot change it now!");
+        }
+    }
+
+    private void applyInterpolator() {
+        if (mInterpolator == null) return;
+
+        long ni;
+        if (mInterpolator.getClass().isAnnotationPresent(HasNativeInterpolator.class)) {
+            ni = ((NativeInterpolatorFactory)mInterpolator).createNativeInterpolator();
+        } else {
+            int duration = nGetDuration(mNativePtr.get());
+            ni = FallbackLUTInterpolator.createNativeInterpolator(mInterpolator, duration);
+        }
+        nSetInterpolator(mNativePtr.get(), ni);
+    }
+
+    private void start(RenderNode node) {
+        if (mStarted) {
+            throw new IllegalStateException("Already started!");
+        }
+        mStarted = true;
+        applyInterpolator();
+        mTarget = node;
+        mTarget.addAnimator(this);
     }
 
     public void start(View target) {
-        mTarget = target.mRenderNode;
-        mTarget.addAnimator(this);
+        start(target.mRenderNode);
         // Kick off a frame to start the process
         target.invalidateViewProperty(true, false);
     }
@@ -106,8 +148,7 @@
             throw new IllegalArgumentException("Not a GLES20RecordingCanvas");
         }
         GLES20RecordingCanvas recordingCanvas = (GLES20RecordingCanvas) canvas;
-        mTarget = recordingCanvas.mNode;
-        mTarget.addAnimator(this);
+        start(recordingCanvas.mNode);
     }
 
     public void cancel() {
@@ -115,11 +156,17 @@
     }
 
     public void setDuration(int duration) {
-        nSetDuration(mNativePtr, duration);
+        checkMutable();
+        nSetDuration(mNativePtr.get(), duration);
+    }
+
+    public void setInterpolator(TimeInterpolator interpolator) {
+        checkMutable();
+        mInterpolator = interpolator;
     }
 
     long getNativeAnimator() {
-        return mNativePtr;
+        return mNativePtr.get();
     }
 
     private void onFinished() {
@@ -134,16 +181,6 @@
         }
     }
 
-    @Override
-    protected void finalize() throws Throwable {
-        try {
-            nUnref(mNativePtr);
-            mNativePtr = 0;
-        } finally {
-            super.finalize();
-        }
-    }
-
     private static native long nCreateAnimator(WeakReference<RenderNodeAnimator> weakThis,
             int property, int deltaValueType, float deltaValue);
     private static native long nCreateCanvasPropertyFloatAnimator(WeakReference<RenderNodeAnimator> weakThis,
@@ -151,5 +188,6 @@
     private static native long nCreateCanvasPropertyPaintAnimator(WeakReference<RenderNodeAnimator> weakThis,
             long canvasProperty, int paintField, int deltaValueType, float deltaValue);
     private static native void nSetDuration(long nativePtr, int duration);
-    private static native void nUnref(long nativePtr);
+    private static native int nGetDuration(long nativePtr);
+    private static native void nSetInterpolator(long animPtr, long interpolatorPtr);
 }
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index 2587ba1..5653066 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -20,6 +20,7 @@
 import android.graphics.Rect;
 import android.graphics.SurfaceTexture;
 import android.os.Trace;
+import android.util.TimeUtils;
 import android.view.Surface.OutOfResourcesException;
 import android.view.View.AttachInfo;
 
@@ -51,8 +52,6 @@
 
     private static final Rect NULL_RECT = new Rect();
 
-    private static final long NANOS_PER_MS = 1000000;
-
     // Keep in sync with DrawFrameTask.h SYNC_* flags
     // Nothing interesting to report
     private static final int SYNC_OK = 0x0;
@@ -203,7 +202,7 @@
     void draw(View view, AttachInfo attachInfo, HardwareDrawCallbacks callbacks, Rect dirty) {
         attachInfo.mIgnoreDirtyState = true;
         long frameTimeNanos = mChoreographer.getFrameTimeNanos();
-        attachInfo.mDrawingTime = frameTimeNanos / NANOS_PER_MS;
+        attachInfo.mDrawingTime = frameTimeNanos / TimeUtils.NANOS_PER_MS;
 
         updateRootDisplayList(view, callbacks);
 
diff --git a/core/java/android/view/animation/AccelerateDecelerateInterpolator.java b/core/java/android/view/animation/AccelerateDecelerateInterpolator.java
index 158c56e..ed6949a 100644
--- a/core/java/android/view/animation/AccelerateDecelerateInterpolator.java
+++ b/core/java/android/view/animation/AccelerateDecelerateInterpolator.java
@@ -19,12 +19,17 @@
 import android.content.Context;
 import android.util.AttributeSet;
 
+import com.android.internal.view.animation.HasNativeInterpolator;
+import com.android.internal.view.animation.NativeInterpolatorFactory;
+import com.android.internal.view.animation.NativeInterpolatorFactoryHelper;
+
 /**
  * An interpolator where the rate of change starts and ends slowly but
  * accelerates through the middle.
  * 
  */
-public class AccelerateDecelerateInterpolator implements Interpolator {
+@HasNativeInterpolator
+public class AccelerateDecelerateInterpolator implements Interpolator, NativeInterpolatorFactory {
     public AccelerateDecelerateInterpolator() {
     }
     
@@ -35,4 +40,10 @@
     public float getInterpolation(float input) {
         return (float)(Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f;
     }
+
+    /** @hide */
+    @Override
+    public long createNativeInterpolator() {
+        return NativeInterpolatorFactoryHelper.createAccelerateDecelerateInterpolator();
+    }
 }
diff --git a/core/java/android/view/animation/AccelerateInterpolator.java b/core/java/android/view/animation/AccelerateInterpolator.java
index dcab743..c08f348 100644
--- a/core/java/android/view/animation/AccelerateInterpolator.java
+++ b/core/java/android/view/animation/AccelerateInterpolator.java
@@ -20,12 +20,17 @@
 import android.content.res.TypedArray;
 import android.util.AttributeSet;
 
+import com.android.internal.view.animation.HasNativeInterpolator;
+import com.android.internal.view.animation.NativeInterpolatorFactory;
+import com.android.internal.view.animation.NativeInterpolatorFactoryHelper;
+
 /**
  * An interpolator where the rate of change starts out slowly and 
  * and then accelerates.
  *
  */
-public class AccelerateInterpolator implements Interpolator {
+@HasNativeInterpolator
+public class AccelerateInterpolator implements Interpolator, NativeInterpolatorFactory {
     private final float mFactor;
     private final double mDoubleFactor;
 
@@ -64,4 +69,10 @@
             return (float)Math.pow(input, mDoubleFactor);
         }
     }
+
+    /** @hide */
+    @Override
+    public long createNativeInterpolator() {
+        return NativeInterpolatorFactoryHelper.createAccelerateInterpolator(mFactor);
+    }
 }
diff --git a/core/java/android/view/animation/AnticipateInterpolator.java b/core/java/android/view/animation/AnticipateInterpolator.java
index a6f110e..83a8007 100644
--- a/core/java/android/view/animation/AnticipateInterpolator.java
+++ b/core/java/android/view/animation/AnticipateInterpolator.java
@@ -20,10 +20,15 @@
 import android.content.res.TypedArray;
 import android.util.AttributeSet;
 
+import com.android.internal.view.animation.HasNativeInterpolator;
+import com.android.internal.view.animation.NativeInterpolatorFactory;
+import com.android.internal.view.animation.NativeInterpolatorFactoryHelper;
+
 /**
  * An interpolator where the change starts backward then flings forward.
  */
-public class AnticipateInterpolator implements Interpolator {
+@HasNativeInterpolator
+public class AnticipateInterpolator implements Interpolator, NativeInterpolatorFactory {
     private final float mTension;
 
     public AnticipateInterpolator() {
@@ -53,4 +58,10 @@
         // a(t) = t * t * ((tension + 1) * t - tension)
         return t * t * ((mTension + 1) * t - mTension);
     }
+
+    /** @hide */
+    @Override
+    public long createNativeInterpolator() {
+        return NativeInterpolatorFactoryHelper.createAnticipateInterpolator(mTension);
+    }
 }
diff --git a/core/java/android/view/animation/AnticipateOvershootInterpolator.java b/core/java/android/view/animation/AnticipateOvershootInterpolator.java
index 3dc9722..1a8adfd 100644
--- a/core/java/android/view/animation/AnticipateOvershootInterpolator.java
+++ b/core/java/android/view/animation/AnticipateOvershootInterpolator.java
@@ -19,6 +19,11 @@
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.util.AttributeSet;
+
+import com.android.internal.view.animation.HasNativeInterpolator;
+import com.android.internal.view.animation.NativeInterpolatorFactory;
+import com.android.internal.view.animation.NativeInterpolatorFactoryHelper;
+
 import static com.android.internal.R.styleable.AnticipateOvershootInterpolator_extraTension;
 import static com.android.internal.R.styleable.AnticipateOvershootInterpolator_tension;
 import static com.android.internal.R.styleable.AnticipateOvershootInterpolator;
@@ -27,7 +32,8 @@
  * An interpolator where the change starts backward then flings forward and overshoots
  * the target value and finally goes back to the final value.
  */
-public class AnticipateOvershootInterpolator implements Interpolator {
+@HasNativeInterpolator
+public class AnticipateOvershootInterpolator implements Interpolator, NativeInterpolatorFactory {
     private final float mTension;
 
     public AnticipateOvershootInterpolator() {
@@ -80,4 +86,10 @@
         if (t < 0.5f) return 0.5f * a(t * 2.0f, mTension);
         else return 0.5f * (o(t * 2.0f - 2.0f, mTension) + 2.0f);
     }
+
+    /** @hide */
+    @Override
+    public long createNativeInterpolator() {
+        return NativeInterpolatorFactoryHelper.createAnticipateOvershootInterpolator(mTension);
+    }
 }
diff --git a/core/java/android/view/animation/BounceInterpolator.java b/core/java/android/view/animation/BounceInterpolator.java
index ecf99a7..9d8ca90 100644
--- a/core/java/android/view/animation/BounceInterpolator.java
+++ b/core/java/android/view/animation/BounceInterpolator.java
@@ -19,10 +19,15 @@
 import android.content.Context;
 import android.util.AttributeSet;
 
+import com.android.internal.view.animation.HasNativeInterpolator;
+import com.android.internal.view.animation.NativeInterpolatorFactory;
+import com.android.internal.view.animation.NativeInterpolatorFactoryHelper;
+
 /**
  * An interpolator where the change bounces at the end.
  */
-public class BounceInterpolator implements Interpolator {
+@HasNativeInterpolator
+public class BounceInterpolator implements Interpolator, NativeInterpolatorFactory {
     public BounceInterpolator() {
     }
 
@@ -47,4 +52,10 @@
         else if (t < 0.9644f) return bounce(t - 0.8526f) + 0.9f;
         else return bounce(t - 1.0435f) + 0.95f;
     }
+
+    /** @hide */
+    @Override
+    public long createNativeInterpolator() {
+        return NativeInterpolatorFactoryHelper.createBounceInterpolator();
+    }
 }
\ No newline at end of file
diff --git a/core/java/android/view/animation/CycleInterpolator.java b/core/java/android/view/animation/CycleInterpolator.java
index d355c23..d1ebf05 100644
--- a/core/java/android/view/animation/CycleInterpolator.java
+++ b/core/java/android/view/animation/CycleInterpolator.java
@@ -20,12 +20,17 @@
 import android.content.res.TypedArray;
 import android.util.AttributeSet;
 
+import com.android.internal.view.animation.HasNativeInterpolator;
+import com.android.internal.view.animation.NativeInterpolatorFactory;
+import com.android.internal.view.animation.NativeInterpolatorFactoryHelper;
+
 /**
  * Repeats the animation for a specified number of cycles. The
  * rate of change follows a sinusoidal pattern.
  *
  */
-public class CycleInterpolator implements Interpolator {
+@HasNativeInterpolator
+public class CycleInterpolator implements Interpolator, NativeInterpolatorFactory {
     public CycleInterpolator(float cycles) {
         mCycles = cycles;
     }
@@ -44,4 +49,10 @@
     }
     
     private float mCycles;
+
+    /** @hide */
+    @Override
+    public long createNativeInterpolator() {
+        return NativeInterpolatorFactoryHelper.createCycleInterpolator(mCycles);
+    }
 }
diff --git a/core/java/android/view/animation/DecelerateInterpolator.java b/core/java/android/view/animation/DecelerateInterpolator.java
index 20e079b..0789a0e 100644
--- a/core/java/android/view/animation/DecelerateInterpolator.java
+++ b/core/java/android/view/animation/DecelerateInterpolator.java
@@ -20,12 +20,17 @@
 import android.content.res.TypedArray;
 import android.util.AttributeSet;
 
+import com.android.internal.view.animation.HasNativeInterpolator;
+import com.android.internal.view.animation.NativeInterpolatorFactory;
+import com.android.internal.view.animation.NativeInterpolatorFactoryHelper;
+
 /**
  * An interpolator where the rate of change starts out quickly and 
  * and then decelerates.
  *
  */
-public class DecelerateInterpolator implements Interpolator {
+@HasNativeInterpolator
+public class DecelerateInterpolator implements Interpolator, NativeInterpolatorFactory {
     public DecelerateInterpolator() {
     }
 
@@ -60,4 +65,10 @@
     }
     
     private float mFactor = 1.0f;
+
+    /** @hide */
+    @Override
+    public long createNativeInterpolator() {
+        return NativeInterpolatorFactoryHelper.createDecelerateInterpolator(mFactor);
+    }
 }
diff --git a/core/java/android/view/animation/LinearInterpolator.java b/core/java/android/view/animation/LinearInterpolator.java
index 96a039f..552c611 100644
--- a/core/java/android/view/animation/LinearInterpolator.java
+++ b/core/java/android/view/animation/LinearInterpolator.java
@@ -19,11 +19,16 @@
 import android.content.Context;
 import android.util.AttributeSet;
 
+import com.android.internal.view.animation.HasNativeInterpolator;
+import com.android.internal.view.animation.NativeInterpolatorFactory;
+import com.android.internal.view.animation.NativeInterpolatorFactoryHelper;
+
 /**
  * An interpolator where the rate of change is constant
  *
  */
-public class LinearInterpolator implements Interpolator {
+@HasNativeInterpolator
+public class LinearInterpolator implements Interpolator, NativeInterpolatorFactory {
 
     public LinearInterpolator() {
     }
@@ -34,4 +39,10 @@
     public float getInterpolation(float input) {
         return input;
     }
+
+    /** @hide */
+    @Override
+    public long createNativeInterpolator() {
+        return NativeInterpolatorFactoryHelper.createLinearInterpolator();
+    }
 }
diff --git a/core/java/android/view/animation/OvershootInterpolator.java b/core/java/android/view/animation/OvershootInterpolator.java
index 494f8ab..a2466f1 100644
--- a/core/java/android/view/animation/OvershootInterpolator.java
+++ b/core/java/android/view/animation/OvershootInterpolator.java
@@ -20,11 +20,16 @@
 import android.content.res.TypedArray;
 import android.util.AttributeSet;
 
+import com.android.internal.view.animation.HasNativeInterpolator;
+import com.android.internal.view.animation.NativeInterpolatorFactory;
+import com.android.internal.view.animation.NativeInterpolatorFactoryHelper;
+
 /**
  * An interpolator where the change flings forward and overshoots the last value
  * then comes back.
  */
-public class OvershootInterpolator implements Interpolator {
+@HasNativeInterpolator
+public class OvershootInterpolator implements Interpolator, NativeInterpolatorFactory {
     private final float mTension;
 
     public OvershootInterpolator() {
@@ -56,4 +61,10 @@
         t -= 1.0f;
         return t * t * ((mTension + 1) * t + mTension) + 1.0f;
     }
+
+    /** @hide */
+    @Override
+    public long createNativeInterpolator() {
+        return NativeInterpolatorFactoryHelper.createOvershootInterpolator(mTension);
+    }
 }
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index e1c6f52..3fee0ac 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -24,6 +24,8 @@
 import com.android.internal.view.IInputMethodSession;
 import com.android.internal.view.InputBindResult;
 
+import libcore.util.Objects;
+
 import android.content.Context;
 import android.graphics.Rect;
 import android.inputmethodservice.InputMethodService;
@@ -49,7 +51,6 @@
 import android.view.KeyEvent;
 import android.view.View;
 import android.view.ViewRootImpl;
-import android.view.inputmethod.CursorAnchorInfo.CursorAnchorInfoBuilder;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -318,11 +319,16 @@
     int mCursorSelEnd;
     int mCursorCandStart;
     int mCursorCandEnd;
+
+    /**
+     * The instance that has previously been sent to the input method.
+     */
+    private CursorAnchorInfo mCursorAnchorInfo = null;
+
     /**
      * The buffer to retrieve the view location in screen coordinates in {@link #updateCursor}.
      */
     private final int[] mViewTopLeft = new int[2];
-    private final CursorAnchorInfoBuilder mCursorAnchorInfoBuilder = new CursorAnchorInfoBuilder();
 
     // -----------------------------------------------------------
     
@@ -492,6 +498,9 @@
                 case SET_CURSOR_ANCHOR_MONITOR_MODE: {
                     synchronized (mH) {
                         mCursorAnchorMonitorMode = msg.arg1;
+                        // Clear the cache.
+                        mCursorRect.setEmpty();
+                        mCursorAnchorInfo = null;
                     }
                     return;
                 }
@@ -1176,6 +1185,7 @@
                 mCursorCandStart = -1;
                 mCursorCandEnd = -1;
                 mCursorRect.setEmpty();
+                mCursorAnchorInfo = null;
                 servedContext = new ControlledInputConnectionWrapper(vh.getLooper(), ic, this);
             } else {
                 servedContext = null;
@@ -1538,10 +1548,9 @@
                     || mCurrentTextBoxAttribute == null || mCurMethod == null) {
                 return;
             }
+            if (DEBUG) Log.d(TAG, "updateCursor");
             mTmpCursorRect.set(left, top, right, bottom);
-            if (!mCursorRect.equals(mTmpCursorRect)) {
-                if (DEBUG) Log.d(TAG, "updateCursor");
-
+            if (!Objects.equal(mCursorRect, mTmpCursorRect)) {
                 try {
                     if (DEBUG) Log.v(TAG, "CURSOR CHANGE: " + mCurMethod);
                     mCursorRect.set(mTmpCursorRect);
@@ -1572,10 +1581,14 @@
                     || mCurrentTextBoxAttribute == null || mCurMethod == null) {
                 return;
             }
-            if (DEBUG) Log.d(TAG, "updateCursorAnchorInfo");
-
+            if (Objects.equal(mCursorAnchorInfo, cursorAnchorInfo)) {
+                Log.w(TAG, "Ignoring redundant updateCursorAnchorInfo: info=" + cursorAnchorInfo);
+                return;
+            }
+            if (DEBUG) Log.v(TAG, "updateCursorAnchorInfo: " + cursorAnchorInfo);
             try {
                 mCurMethod.updateCursorAnchorInfo(cursorAnchorInfo);
+                mCursorAnchorInfo = cursorAnchorInfo;
             } catch (RemoteException e) {
                 Log.w(TAG, "IME died: " + mCurId, e);
             }
diff --git a/core/java/android/webkit/EventLogTags.logtags b/core/java/android/webkit/EventLogTags.logtags
index b0b5493..a90aebd 100644
--- a/core/java/android/webkit/EventLogTags.logtags
+++ b/core/java/android/webkit/EventLogTags.logtags
@@ -8,3 +8,4 @@
 # 70103- used by the browser app itself
 
 70150 browser_snap_center
+70151 exp_det_attempt_to_call_object_getclass (app_signature|3)
diff --git a/core/java/android/webkit/PermissionRequest.java b/core/java/android/webkit/PermissionRequest.java
index 3e33498..fa760b7 100644
--- a/core/java/android/webkit/PermissionRequest.java
+++ b/core/java/android/webkit/PermissionRequest.java
@@ -28,6 +28,7 @@
 public interface PermissionRequest {
     /**
      * Resource belongs to geolocation service.
+     * @hide - see b/14668406
      */
     public final static long RESOURCE_GEOLOCATION = 1 << 0;
     /**
diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java
index 25bcd44..ac12357 100644
--- a/core/java/android/webkit/WebViewFactory.java
+++ b/core/java/android/webkit/WebViewFactory.java
@@ -50,28 +50,6 @@
     private static WebViewFactoryProvider sProviderInstance;
     private static final Object sProviderLock = new Object();
 
-    public static boolean isExperimentalWebViewAvailable() {
-        // TODO: Remove callers of this method then remove it.
-        return false;  // Hide the toggle in Developer Settings.
-    }
-
-    /** @hide */
-    public static void setUseExperimentalWebView(boolean enable) {
-        // TODO: Remove callers of this method then remove it.
-    }
-
-    /** @hide */
-    public static boolean useExperimentalWebView() {
-        // TODO: Remove callers of this method then remove it.
-        return true;
-    }
-
-    /** @hide */
-    public static boolean isUseExperimentalWebViewSet() {
-        // TODO: Remove callers of this method then remove it.
-        return false;  // User has not modifed Developer Settings
-    }
-
     static WebViewFactoryProvider getProvider() {
         synchronized (sProviderLock) {
             // For now the main purpose of this function (and the factory abstraction) is to keep
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index f4cd5fc..d9a4f57 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -743,7 +743,7 @@
          *
          * @param view The view whose scroll state is being reported
          *
-         * @param scrollState The current scroll state. One of 
+         * @param scrollState The current scroll state. One of
          * {@link #SCROLL_STATE_TOUCH_SCROLL} or {@link #SCROLL_STATE_IDLE}.
          */
         public void onScrollStateChanged(AbsListView view, int scrollState);
@@ -3267,7 +3267,7 @@
         }
     }
 
-    private boolean startScrollIfNeeded(int y, MotionEvent vtev) {
+    private boolean startScrollIfNeeded(int x, int y, MotionEvent vtev) {
         // Check if we have moved far enough that it looks more like a
         // scroll than a tap
         final int deltaY = y - mMotionY;
@@ -3296,27 +3296,31 @@
             if (parent != null) {
                 parent.requestDisallowInterceptTouchEvent(true);
             }
-            scrollIfNeeded(y, vtev);
+            scrollIfNeeded(x, y, vtev);
             return true;
         }
 
         return false;
     }
 
-    private void scrollIfNeeded(int y, MotionEvent vtev) {
+    private void scrollIfNeeded(int x, int y, MotionEvent vtev) {
         int rawDeltaY = y - mMotionY;
+        int scrollOffsetCorrection = 0;
+        int scrollConsumedCorrection = 0;
+        if (mLastY == Integer.MIN_VALUE) {
+            rawDeltaY -= mMotionCorrection;
+        }
         if (dispatchNestedPreScroll(0, rawDeltaY, mScrollConsumed, mScrollOffset)) {
             rawDeltaY -= mScrollConsumed[1];
-            mMotionCorrection -= mScrollOffset[1];
-            if (mLastY != Integer.MIN_VALUE) {
-                mLastY -= mScrollOffset[1] + mScrollConsumed[1];
-            }
+            scrollOffsetCorrection -= mScrollOffset[1];
+            scrollConsumedCorrection -= mScrollConsumed[1];
             if (vtev != null) {
                 vtev.offsetLocation(0, mScrollOffset[1]);
             }
         }
-        final int deltaY = rawDeltaY - mMotionCorrection;
-        int incrementalDeltaY = mLastY != Integer.MIN_VALUE ? y - mLastY : deltaY;
+        final int deltaY = rawDeltaY;
+        int incrementalDeltaY =
+                mLastY != Integer.MIN_VALUE ? y - mLastY + scrollConsumedCorrection : deltaY;
         int lastYCorrection = 0;
 
         if (mTouchMode == TOUCH_MODE_SCROLL) {
@@ -3378,46 +3382,51 @@
                                 (motionViewRealTop - motionViewPrevTop);
                         if (dispatchNestedScroll(0, overscroll - incrementalDeltaY, 0, overscroll,
                                 mScrollOffset)) {
-                            mMotionCorrection -= mScrollOffset[1];
                             lastYCorrection -= mScrollOffset[1];
                             if (vtev != null) {
                                 vtev.offsetLocation(0, mScrollOffset[1]);
                             }
                         } else {
-                            overScrollBy(0, overscroll, 0, mScrollY, 0, 0,
-                                    0, mOverscrollDistance, true);
-                            if (Math.abs(mOverscrollDistance) == Math.abs(mScrollY)) {
-                                // Don't allow overfling if we're at the edge.
-                                if (mVelocityTracker != null) {
-                                    mVelocityTracker.clear();
-                                }
+                            final boolean atOverscrollEdge = overScrollBy(0, overscroll,
+                                    0, mScrollY, 0, 0, 0, mOverscrollDistance, true);
+
+                            if (atOverscrollEdge && mVelocityTracker != null) {
+                                // Don't allow overfling if we're at the edge
+                                mVelocityTracker.clear();
                             }
 
                             final int overscrollMode = getOverScrollMode();
                             if (overscrollMode == OVER_SCROLL_ALWAYS ||
                                     (overscrollMode == OVER_SCROLL_IF_CONTENT_SCROLLS &&
                                             !contentFits())) {
-                                mDirection = 0; // Reset when entering overscroll.
-                                mTouchMode = TOUCH_MODE_OVERSCROLL;
-                                if (deltaY > 0) {
-                                    mEdgeGlowTop.onPull((float) overscroll / getHeight());
+                                if (!atOverscrollEdge) {
+                                    mDirection = 0; // Reset when entering overscroll.
+                                    mTouchMode = TOUCH_MODE_OVERSCROLL;
+                                }
+                                if (incrementalDeltaY > 0) {
+                                    mEdgeGlowTop.onPull((float) overscroll / getHeight(),
+                                            (float) x / getWidth());
                                     if (!mEdgeGlowBottom.isFinished()) {
                                         mEdgeGlowBottom.onRelease();
                                     }
-                                    invalidate(mEdgeGlowTop.getBounds(false));
-                                } else if (deltaY < 0) {
-                                    mEdgeGlowBottom.onPull((float) overscroll / getHeight());
+                                    invalidate(0, 0, getWidth(),
+                                            mEdgeGlowTop.getMaxHeight() + getPaddingTop());
+                                } else if (incrementalDeltaY < 0) {
+                                    mEdgeGlowBottom.onPull((float) overscroll / getHeight(),
+                                            1.f - (float) x / getWidth());
                                     if (!mEdgeGlowTop.isFinished()) {
                                         mEdgeGlowTop.onRelease();
                                     }
-                                    invalidate(mEdgeGlowBottom.getBounds(true));
+                                    invalidate(0, getHeight() - getPaddingBottom() -
+                                            mEdgeGlowBottom.getMaxHeight(), getWidth(),
+                                            getHeight());
                                 }
                             }
                         }
                     }
-                    mMotionY = y;
+                    mMotionY = y + scrollOffsetCorrection;
                 }
-                mLastY = y + lastYCorrection;
+                mLastY = y + lastYCorrection + scrollOffsetCorrection;
             }
         } else if (mTouchMode == TOUCH_MODE_OVERSCROLL) {
             if (y != mLastY) {
@@ -3445,17 +3454,22 @@
                             (overscrollMode == OVER_SCROLL_IF_CONTENT_SCROLLS &&
                                     !contentFits())) {
                         if (rawDeltaY > 0) {
-                            mEdgeGlowTop.onPull((float) overScrollDistance / getHeight());
+                            mEdgeGlowTop.onPull((float) overScrollDistance / getHeight(),
+                                    (float) x / getWidth());
                             if (!mEdgeGlowBottom.isFinished()) {
                                 mEdgeGlowBottom.onRelease();
                             }
-                            invalidate(mEdgeGlowTop.getBounds(false));
+                            invalidate(0, 0, getWidth(),
+                                    mEdgeGlowTop.getMaxHeight() + getPaddingTop());
                         } else if (rawDeltaY < 0) {
-                            mEdgeGlowBottom.onPull((float) overScrollDistance / getHeight());
+                            mEdgeGlowBottom.onPull((float) overScrollDistance / getHeight(),
+                                    1.f - (float) x / getWidth());
                             if (!mEdgeGlowTop.isFinished()) {
                                 mEdgeGlowTop.onRelease();
                             }
-                            invalidate(mEdgeGlowBottom.getBounds(true));
+                            invalidate(0, getHeight() - getPaddingBottom() -
+                                    mEdgeGlowBottom.getMaxHeight(), getWidth(),
+                                    getHeight());
                         }
                     }
                 }
@@ -3703,7 +3717,7 @@
             case TOUCH_MODE_DONE_WAITING:
                 // Check if we have moved far enough that it looks more like a
                 // scroll than a tap. If so, we'll enter scrolling mode.
-                if (startScrollIfNeeded(y, vtev)) {
+                if (startScrollIfNeeded((int) ev.getX(pointerIndex), y, vtev)) {
                     break;
                 }
                 // Otherwise, check containment within list bounds. If we're
@@ -3723,7 +3737,7 @@
                 break;
             case TOUCH_MODE_SCROLL:
             case TOUCH_MODE_OVERSCROLL:
-                scrollIfNeeded(y, vtev);
+                scrollIfNeeded((int) ev.getX(pointerIndex), y, vtev);
                 break;
         }
     }
@@ -4022,8 +4036,8 @@
                 canvas.translate(leftPadding, edgeY);
                 mEdgeGlowTop.setSize(width, getHeight());
                 if (mEdgeGlowTop.draw(canvas)) {
-                    mEdgeGlowTop.setPosition(leftPadding, edgeY);
-                    invalidate(mEdgeGlowTop.getBounds(false));
+                    invalidate(0, 0, getWidth(),
+                            mEdgeGlowTop.getMaxHeight() + getPaddingTop());
                 }
                 canvas.restoreToCount(restoreCount);
             }
@@ -4040,9 +4054,9 @@
                 canvas.rotate(180, width, 0);
                 mEdgeGlowBottom.setSize(width, height);
                 if (mEdgeGlowBottom.draw(canvas)) {
-                    // Account for the rotation
-                    mEdgeGlowBottom.setPosition(edgeX + width, edgeY);
-                    invalidate(mEdgeGlowBottom.getBounds(true));
+                    invalidate(0, getHeight() - getPaddingBottom() -
+                            mEdgeGlowBottom.getMaxHeight(), getWidth(),
+                            getHeight());
                 }
                 canvas.restoreToCount(restoreCount);
             }
@@ -4161,7 +4175,7 @@
                 final int y = (int) ev.getY(pointerIndex);
                 initVelocityTrackerIfNotExists();
                 mVelocityTracker.addMovement(ev);
-                if (startScrollIfNeeded(y, null)) {
+                if (startScrollIfNeeded((int) ev.getX(pointerIndex), y, null)) {
                     return true;
                 }
                 break;
diff --git a/core/java/android/widget/AbsSeekBar.java b/core/java/android/widget/AbsSeekBar.java
index 225cd6d..4f2d9c6 100644
--- a/core/java/android/widget/AbsSeekBar.java
+++ b/core/java/android/widget/AbsSeekBar.java
@@ -19,7 +19,9 @@
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.graphics.Canvas;
+import android.graphics.Insets;
 import android.graphics.Rect;
+import android.graphics.Region.Op;
 import android.graphics.drawable.Drawable;
 import android.os.Bundle;
 import android.util.AttributeSet;
@@ -32,9 +34,12 @@
 import com.android.internal.R;
 
 public abstract class AbsSeekBar extends ProgressBar {
+    private final Rect mTempRect = new Rect();
+
     private Drawable mThumb;
     private int mThumbOffset;
-    
+    private boolean mSplitTrack;
+
     /**
      * On touch, this offset plus the scaled value from the position of the
      * touch will form the progress value. Usually 0.
@@ -51,10 +56,10 @@
      * progress.
      */
     private int mKeyProgressIncrement = 1;
-    
+
     private static final int NO_ALPHA = 0xFF;
     private float mDisabledAlpha;
-    
+
     private int mScaledTouchSlop;
     private float mTouchDownX;
     private boolean mIsDragging;
@@ -76,12 +81,16 @@
 
         TypedArray a = context.obtainStyledAttributes(
                 attrs, com.android.internal.R.styleable.SeekBar, defStyleAttr, defStyleRes);
-        Drawable thumb = a.getDrawable(com.android.internal.R.styleable.SeekBar_thumb);
-        setThumb(thumb); // will guess mThumbOffset if thumb != null...
-        // ...but allow layout to override this
-        int thumbOffset = a.getDimensionPixelOffset(
+
+        final Drawable thumb = a.getDrawable(com.android.internal.R.styleable.SeekBar_thumb);
+        setThumb(thumb);
+
+        // Guess thumb offset if thumb != null, but allow layout to override.
+        final int thumbOffset = a.getDimensionPixelOffset(
                 com.android.internal.R.styleable.SeekBar_thumbOffset, getThumbOffset());
         setThumbOffset(thumbOffset);
+
+        mSplitTrack = a.getBoolean(com.android.internal.R.styleable.SeekBar_splitTrack, false);
         a.recycle();
 
         a = context.obtainStyledAttributes(attrs,
@@ -97,7 +106,7 @@
      * <p>
      * If the thumb is a valid drawable (i.e. not null), half its width will be
      * used as the new thumb offset (@see #setThumbOffset(int)).
-     * 
+     *
      * @param thumb Drawable representing the thumb
      */
     public void setThumb(Drawable thumb) {
@@ -132,7 +141,7 @@
         mThumb = thumb;
         invalidate();
         if (needUpdate) {
-            updateThumbPos(getWidth(), getHeight());
+            updateThumbAndTrackPos(getWidth(), getHeight());
             if (thumb != null && thumb.isStateful()) {
                 // Note that if the states are different this won't work.
                 // For now, let's consider that an app bug.
@@ -162,7 +171,7 @@
     /**
      * Sets the thumb offset that allows the thumb to extend out of the range of
      * the track.
-     * 
+     *
      * @param thumbOffset The offset amount in pixels.
      */
     public void setThumbOffset(int thumbOffset) {
@@ -171,8 +180,27 @@
     }
 
     /**
+     * Specifies whether the track should be split by the thumb. When true,
+     * the thumb's optical bounds will be clipped out of the track drawable,
+     * then the thumb will be drawn into the resulting gap.
+     *
+     * @param splitTrack Whether the track should be split by the thumb
+     */
+    public void setSplitTrack(boolean splitTrack) {
+        mSplitTrack = splitTrack;
+        invalidate();
+    }
+
+    /**
+     * Returns whether the track should be split by the thumb.
+     */
+    public boolean getSplitTrack() {
+        return mSplitTrack;
+    }
+
+    /**
      * Sets the amount of progress changed via the arrow keys.
-     * 
+     *
      * @param increment The amount to increment or decrement when the user
      *            presses the arrow keys.
      */
@@ -184,14 +212,14 @@
      * Returns the amount of progress changed via the arrow keys.
      * <p>
      * By default, this will be a value that is derived from the max progress.
-     * 
+     *
      * @return The amount to increment or decrement when the user presses the
      *         arrow keys. This will be positive.
      */
     public int getKeyProgressIncrement() {
         return mKeyProgressIncrement;
     }
-    
+
     @Override
     public synchronized void setMax(int max) {
         super.setMax(max);
@@ -217,79 +245,85 @@
     @Override
     protected void drawableStateChanged() {
         super.drawableStateChanged();
-        
-        Drawable progressDrawable = getProgressDrawable();
+
+        final Drawable progressDrawable = getProgressDrawable();
         if (progressDrawable != null) {
             progressDrawable.setAlpha(isEnabled() ? NO_ALPHA : (int) (NO_ALPHA * mDisabledAlpha));
         }
-        
-        if (mThumb != null && mThumb.isStateful()) {
-            int[] state = getDrawableState();
-            mThumb.setState(state);
+
+        final Drawable thumb = mThumb;
+        if (thumb != null && thumb.isStateful()) {
+            thumb.setState(getDrawableState());
         }
     }
-    
+
     @Override
     void onProgressRefresh(float scale, boolean fromUser) {
         super.onProgressRefresh(scale, fromUser);
-        Drawable thumb = mThumb;
+
+        final Drawable thumb = mThumb;
         if (thumb != null) {
             setThumbPos(getWidth(), thumb, scale, Integer.MIN_VALUE);
-            /*
-             * Since we draw translated, the drawable's bounds that it signals
-             * for invalidation won't be the actual bounds we want invalidated,
-             * so just invalidate this whole view.
-             */
+
+            // Since we draw translated, the drawable's bounds that it signals
+            // for invalidation won't be the actual bounds we want invalidated,
+            // so just invalidate this whole view.
             invalidate();
         }
     }
-    
-    
+
     @Override
     protected void onSizeChanged(int w, int h, int oldw, int oldh) {
         super.onSizeChanged(w, h, oldw, oldh);
-        updateThumbPos(w, h);
+
+        updateThumbAndTrackPos(w, h);
     }
 
-    private void updateThumbPos(int w, int h) {
-        Drawable d = getCurrentDrawable();
-        Drawable thumb = mThumb;
-        int thumbHeight = thumb == null ? 0 : thumb.getIntrinsicHeight();
+    private void updateThumbAndTrackPos(int w, int h) {
+        final Drawable track = getCurrentDrawable();
+        final Drawable thumb = mThumb;
+
         // The max height does not incorporate padding, whereas the height
-        // parameter does
-        int trackHeight = Math.min(mMaxHeight, h - mPaddingTop - mPaddingBottom);
-        
-        int max = getMax();
-        float scale = max > 0 ? (float) getProgress() / (float) max : 0;
-        
+        // parameter does.
+        final int trackHeight = Math.min(mMaxHeight, h - mPaddingTop - mPaddingBottom);
+        final int thumbHeight = thumb == null ? 0 : thumb.getIntrinsicHeight();
+
+        // Apply offset to whichever item is taller.
+        final int trackOffset;
+        final int thumbOffset;
         if (thumbHeight > trackHeight) {
-            if (thumb != null) {
-                setThumbPos(w, thumb, scale, 0);
-            }
-            int gapForCenteringTrack = (thumbHeight - trackHeight) / 2;
-            if (d != null) {
-                // Canvas will be translated by the padding, so 0,0 is where we start drawing
-                d.setBounds(0, gapForCenteringTrack, 
-                        w - mPaddingRight - mPaddingLeft, h - mPaddingBottom - gapForCenteringTrack
-                        - mPaddingTop);
-            }
+            trackOffset = (thumbHeight - trackHeight) / 2;
+            thumbOffset = 0;
         } else {
-            if (d != null) {
-                // Canvas will be translated by the padding, so 0,0 is where we start drawing
-                d.setBounds(0, 0, w - mPaddingRight - mPaddingLeft, h - mPaddingBottom
-                        - mPaddingTop);
-            }
-            int gap = (trackHeight - thumbHeight) / 2;
-            if (thumb != null) {
-                setThumbPos(w, thumb, scale, gap);
-            }
+            trackOffset = 0;
+            thumbOffset = (trackHeight - thumbHeight) / 2;
+        }
+
+        if (track != null) {
+            track.setBounds(0, trackOffset, w - mPaddingRight - mPaddingLeft,
+                    h - mPaddingBottom - trackOffset - mPaddingTop);
+        }
+
+        if (thumb != null) {
+            setThumbPos(w, thumb, getScale(), thumbOffset);
         }
     }
 
+    private float getScale() {
+        final int max = getMax();
+        return max > 0 ? getProgress() / (float) max : 0;
+    }
+
     /**
-     * @param gap If set to {@link Integer#MIN_VALUE}, this will be ignored and
+     * Updates the thumb drawable bounds.
+     *
+     * @param w Width of the view, including padding
+     * @param thumb Drawable used for the thumb
+     * @param scale Current progress between 0 and 1
+     * @param offset Vertical offset for centering. If set to
+     *            {@link Integer#MIN_VALUE}, the current offset will be used.
      */
-    private void setThumbPos(int w, Drawable thumb, float scale, int gap) {
+    private void setThumbPos(int w, Drawable thumb, float scale, int offset) {
         int available = w - mPaddingLeft - mPaddingRight;
         final int thumbWidth = thumb.getIntrinsicWidth();
         final int thumbHeight = thumb.getIntrinsicHeight();
@@ -301,13 +335,13 @@
         final int thumbPos = (int) (scale * available + 0.5f);
 
         final int top, bottom;
-        if (gap == Integer.MIN_VALUE) {
+        if (offset == Integer.MIN_VALUE) {
             final Rect oldBounds = thumb.getBounds();
             top = oldBounds.top;
             bottom = oldBounds.bottom;
         } else {
-            top = gap;
-            bottom = gap + thumbHeight;
+            top = offset;
+            bottom = offset + thumbHeight;
         }
 
         final int left = (isLayoutRtl() && mMirrorForRtl) ? available - thumbPos : thumbPos;
@@ -342,6 +376,33 @@
     protected synchronized void onDraw(Canvas canvas) {
         super.onDraw(canvas);
 
+        drawThumb(canvas);
+    }
+
+    @Override
+    void drawTrack(Canvas canvas) {
+        final Drawable thumbDrawable = mThumb;
+        if (thumbDrawable != null && mSplitTrack) {
+            final Insets insets = thumbDrawable.getOpticalInsets();
+            final Rect tempRect = mTempRect;
+            thumbDrawable.copyBounds(tempRect);
+            tempRect.offset(mPaddingLeft - mThumbOffset, mPaddingTop);
+            tempRect.left += insets.left;
+            tempRect.right -= insets.right;
+
+            final int saveCount = canvas.save();
+            canvas.clipRect(tempRect, Op.DIFFERENCE);
+            super.drawTrack(canvas);
+            canvas.restoreToCount(saveCount);
+        } else {
+            super.drawTrack(canvas);
+        }
+    }
+
+    /**
+     * Draw the thumb.
+     */
+    void drawThumb(Canvas canvas) {
         if (mThumb != null) {
             canvas.save();
             // Translate the padding. For the x, we need to allow the thumb to
@@ -366,17 +427,17 @@
         }
         dw += mPaddingLeft + mPaddingRight;
         dh += mPaddingTop + mPaddingBottom;
-        
+
         setMeasuredDimension(resolveSizeAndState(dw, widthMeasureSpec, 0),
                 resolveSizeAndState(dh, heightMeasureSpec, 0));
     }
-    
+
     @Override
     public boolean onTouchEvent(MotionEvent event) {
         if (!mIsUserSeekable || !isEnabled()) {
             return false;
         }
-        
+
         switch (event.getAction()) {
             case MotionEvent.ACTION_DOWN:
                 if (isInScrollingContainer()) {
@@ -391,7 +452,7 @@
                     attemptClaimDrag();
                 }
                 break;
-                
+
             case MotionEvent.ACTION_MOVE:
                 if (mIsDragging) {
                     trackTouchEvent(event);
@@ -408,7 +469,7 @@
                     }
                 }
                 break;
-                
+
             case MotionEvent.ACTION_UP:
                 if (mIsDragging) {
                     trackTouchEvent(event);
@@ -426,7 +487,7 @@
                 // value has not apparently changed)
                 invalidate();
                 break;
-                
+
             case MotionEvent.ACTION_CANCEL:
                 if (mIsDragging) {
                     onStopTrackingTouch();
@@ -493,7 +554,7 @@
             mParent.requestDisallowInterceptTouchEvent(true);
         }
     }
-    
+
     /**
      * This is called when the user has started touching this widget.
      */
@@ -526,7 +587,7 @@
                     setProgress(progress - mKeyProgressIncrement, true);
                     onKeyChange();
                     return true;
-            
+
                 case KeyEvent.KEYCODE_DPAD_RIGHT:
                     if (progress >= getMax()) break;
                     setProgress(progress + mKeyProgressIncrement, true);
@@ -595,17 +656,13 @@
     public void onRtlPropertiesChanged(int layoutDirection) {
         super.onRtlPropertiesChanged(layoutDirection);
 
-        int max = getMax();
-        float scale = max > 0 ? (float) getProgress() / (float) max : 0;
-
-        Drawable thumb = mThumb;
+        final Drawable thumb = mThumb;
         if (thumb != null) {
-            setThumbPos(getWidth(), thumb, scale, Integer.MIN_VALUE);
-            /*
-             * Since we draw translated, the drawable's bounds that it signals
-             * for invalidation won't be the actual bounds we want invalidated,
-             * so just invalidate this whole view.
-             */
+            setThumbPos(getWidth(), thumb, getScale(), Integer.MIN_VALUE);
+
+            // Since we draw translated, the drawable's bounds that it signals
+            // for invalidation won't be the actual bounds we want invalidated,
+            // so just invalidate this whole view.
             invalidate();
         }
     }
diff --git a/core/java/android/widget/EdgeEffect.java b/core/java/android/widget/EdgeEffect.java
index fa37443..83fbe8f 100644
--- a/core/java/android/widget/EdgeEffect.java
+++ b/core/java/android/widget/EdgeEffect.java
@@ -16,7 +16,14 @@
 
 package android.widget;
 
+import android.content.res.TypedArray;
+import android.graphics.Paint;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffXfermode;
 import android.graphics.Rect;
+import android.graphics.RectF;
+import android.graphics.Xfermode;
+import android.util.Log;
 import com.android.internal.R;
 
 import android.content.Context;
@@ -59,12 +66,10 @@
     private static final int PULL_DECAY_TIME = 1000;
 
     private static final float MAX_ALPHA = 1.f;
-    private static final float HELD_EDGE_SCALE_Y = 0.5f;
 
-    private static final float MAX_GLOW_HEIGHT = 4.f;
+    private static final float MAX_GLOW_HEIGHT = 1.5f;
 
-    private static final float PULL_GLOW_BEGIN = 1.f;
-    private static final float PULL_EDGE_BEGIN = 0.6f;
+    private static final float PULL_GLOW_BEGIN = 0.f;
 
     // Minimum velocity that will be absorbed
     private static final int MIN_VELOCITY = 100;
@@ -73,24 +78,11 @@
 
     private static final float EPSILON = 0.001f;
 
-    private final Drawable mEdge;
-    private final Drawable mGlow;
-    private int mWidth;
-    private int mHeight;
-    private int mX;
-    private int mY;
-    private static final int MIN_WIDTH = 300;
-    private final int mMinWidth;
+    private static final float SIN_45 = (float) Math.sin(Math.PI / 4);
 
-    private float mEdgeAlpha;
-    private float mEdgeScaleY;
     private float mGlowAlpha;
     private float mGlowScaleY;
 
-    private float mEdgeAlphaStart;
-    private float mEdgeAlphaFinish;
-    private float mEdgeScaleYStart;
-    private float mEdgeScaleYFinish;
     private float mGlowAlphaStart;
     private float mGlowAlphaFinish;
     private float mGlowScaleYStart;
@@ -107,16 +99,11 @@
     private static final int STATE_RECEDE = 3;
     private static final int STATE_PULL_DECAY = 4;
 
-    // How much dragging should effect the height of the edge image.
-    // Number determined by user testing.
-    private static final int PULL_DISTANCE_EDGE_FACTOR = 7;
-
     // How much dragging should effect the height of the glow image.
     // Number determined by user testing.
     private static final int PULL_DISTANCE_GLOW_FACTOR = 7;
     private static final float PULL_DISTANCE_ALPHA_GLOW_FACTOR = 1.1f;
 
-    private static final int VELOCITY_EDGE_FACTOR = 8;
     private static final int VELOCITY_GLOW_FACTOR = 12;
 
     private int mState = STATE_IDLE;
@@ -124,30 +111,26 @@
     private float mPullDistance;
     
     private final Rect mBounds = new Rect();
-
-    private final int mEdgeHeight;
-    private final int mGlowHeight;
-    private final int mGlowWidth;
-    private final int mMaxEffectHeight;
+    private final RectF mArcRect = new RectF();
+    private final Paint mPaint = new Paint();
+    private float mRadius;
+    private float mDisplacement = 0.5f;
+    private float mTargetDisplacement = 0.5f;
 
     /**
      * Construct a new EdgeEffect with a theme appropriate for the provided context.
      * @param context Context used to provide theming and resource information for the EdgeEffect
      */
     public EdgeEffect(Context context) {
-        final Resources res = context.getResources();
-        mEdge = context.getDrawable(R.drawable.overscroll_edge);
-        mGlow = context.getDrawable(R.drawable.overscroll_glow);
-
-        mEdgeHeight = mEdge.getIntrinsicHeight();
-        mGlowHeight = mGlow.getIntrinsicHeight();
-        mGlowWidth = mGlow.getIntrinsicWidth();
-
-        mMaxEffectHeight = (int) (Math.min(
-                mGlowHeight * MAX_GLOW_HEIGHT * mGlowHeight / mGlowWidth * 0.6f,
-                mGlowHeight * MAX_GLOW_HEIGHT) + 0.5f);
-
-        mMinWidth = (int) (res.getDisplayMetrics().density * MIN_WIDTH + 0.5f);
+        mPaint.setAntiAlias(true);
+        final TypedArray a = context.obtainStyledAttributes(
+                com.android.internal.R.styleable.EdgeEffect);
+        final int themeColor = a.getColor(
+                com.android.internal.R.styleable.EdgeEffect_colorPrimaryLight, 0xff666666);
+        a.recycle();
+        mPaint.setColor((themeColor & 0xffffff) | 0x66000000);
+        mPaint.setStyle(Paint.Style.FILL);
+        mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_ATOP));
         mInterpolator = new DecelerateInterpolator();
     }
 
@@ -158,20 +141,12 @@
      * @param height Effect height in pixels
      */
     public void setSize(int width, int height) {
-        mWidth = width;
-        mHeight = height;
-    }
+        final float r = width * 0.5f / SIN_45;
+        final float y = SIN_45 * r;
+        final float h = r - y;
+        mRadius = r;
 
-    /**
-     * Set the position of this edge effect in pixels. This position is
-     * only used by {@link #getBounds(boolean)}.
-     * 
-     * @param x The position of the edge effect on the X axis
-     * @param y The position of the edge effect on the Y axis
-     */
-    void setPosition(int x, int y) {
-        mX = x;
-        mY = y;
+        mBounds.set(mBounds.left, mBounds.top, width, (int) Math.min(height, h));
     }
 
     /**
@@ -199,17 +174,38 @@
      * The host view should always {@link android.view.View#invalidate()} after this
      * and draw the results accordingly.
      *
+     * <p>Views using EdgeEffect should favor {@link #onPull(float, float)} when the displacement
+     * of the pull point is known.</p>
+     *
      * @param deltaDistance Change in distance since the last call. Values may be 0 (no change) to
      *                      1.f (full length of the view) or negative values to express change
      *                      back toward the edge reached to initiate the effect.
      */
     public void onPull(float deltaDistance) {
+        onPull(deltaDistance, 0.5f);
+    }
+
+    /**
+     * A view should call this when content is pulled away from an edge by the user.
+     * This will update the state of the current visual effect and its associated animation.
+     * The host view should always {@link android.view.View#invalidate()} after this
+     * and draw the results accordingly.
+     *
+     * @param deltaDistance Change in distance since the last call. Values may be 0 (no change) to
+     *                      1.f (full length of the view) or negative values to express change
+     *                      back toward the edge reached to initiate the effect.
+     * @param displacement The displacement from the starting side of the effect of the point
+     *                     initiating the pull. In the case of touch this is the finger position.
+     *                     Values may be from 0-1.
+     */
+    public void onPull(float deltaDistance, float displacement) {
         final long now = AnimationUtils.currentAnimationTimeMillis();
+        mTargetDisplacement = displacement;
         if (mState == STATE_PULL_DECAY && now - mStartTime < mDuration) {
             return;
         }
         if (mState != STATE_PULL) {
-            mGlowScaleY = PULL_GLOW_BEGIN;
+            mGlowScaleY = Math.max(PULL_GLOW_BEGIN, mGlowScaleY);
         }
         mState = STATE_PULL;
 
@@ -217,15 +213,10 @@
         mDuration = PULL_TIME;
 
         mPullDistance += deltaDistance;
-        float distance = Math.abs(mPullDistance);
-
-        mEdgeAlpha = mEdgeAlphaStart = Math.max(PULL_EDGE_BEGIN, Math.min(distance, MAX_ALPHA));
-        mEdgeScaleY = mEdgeScaleYStart = Math.max(
-                HELD_EDGE_SCALE_Y, Math.min(distance * PULL_DISTANCE_EDGE_FACTOR, 1.f));
 
         mGlowAlpha = mGlowAlphaStart = Math.min(MAX_ALPHA,
                 mGlowAlpha +
-                (Math.abs(deltaDistance) * PULL_DISTANCE_ALPHA_GLOW_FACTOR));
+                        (Math.abs(deltaDistance) * PULL_DISTANCE_ALPHA_GLOW_FACTOR));
 
         float glowChange = Math.abs(deltaDistance);
         if (deltaDistance > 0 && mPullDistance < 0) {
@@ -239,8 +230,6 @@
         mGlowScaleY = mGlowScaleYStart = Math.min(MAX_GLOW_HEIGHT, Math.max(
                 0, mGlowScaleY + glowChange * PULL_DISTANCE_GLOW_FACTOR));
 
-        mEdgeAlphaFinish = mEdgeAlpha;
-        mEdgeScaleYFinish = mEdgeScaleY;
         mGlowAlphaFinish = mGlowAlpha;
         mGlowScaleYFinish = mGlowScaleY;
     }
@@ -259,13 +248,9 @@
         }
 
         mState = STATE_RECEDE;
-        mEdgeAlphaStart = mEdgeAlpha;
-        mEdgeScaleYStart = mEdgeScaleY;
         mGlowAlphaStart = mGlowAlpha;
         mGlowScaleYStart = mGlowScaleY;
 
-        mEdgeAlphaFinish = 0.f;
-        mEdgeScaleYFinish = 0.f;
         mGlowAlphaFinish = 0.f;
         mGlowScaleYFinish = 0.f;
 
@@ -290,30 +275,21 @@
         mStartTime = AnimationUtils.currentAnimationTimeMillis();
         mDuration = 0.15f + (velocity * 0.02f);
 
-        // The edge should always be at least partially visible, regardless
-        // of velocity.
-        mEdgeAlphaStart = 0.f;
-        mEdgeScaleY = mEdgeScaleYStart = 0.f;
         // The glow depends more on the velocity, and therefore starts out
         // nearly invisible.
         mGlowAlphaStart = 0.3f;
-        mGlowScaleYStart = 0.f;
+        mGlowScaleYStart = Math.max(mGlowScaleY, 0.f);
 
-        // Factor the velocity by 8. Testing on device shows this works best to
-        // reflect the strength of the user's scrolling.
-        mEdgeAlphaFinish = Math.max(0, Math.min(velocity * VELOCITY_EDGE_FACTOR, 1));
-        // Edge should never get larger than the size of its asset.
-        mEdgeScaleYFinish = Math.max(
-                HELD_EDGE_SCALE_Y, Math.min(velocity * VELOCITY_EDGE_FACTOR, 1.f));
 
         // Growth for the size of the glow should be quadratic to properly
         // respond
         // to a user's scrolling speed. The faster the scrolling speed, the more
         // intense the effect should be for both the size and the saturation.
-        mGlowScaleYFinish = Math.min(0.025f + (velocity * (velocity / 100) * 0.00015f), 1.75f);
+        mGlowScaleYFinish = Math.min(0.025f + (velocity * (velocity / 100) * 0.00015f) / 2, 1.f);
         // Alpha should change for the glow as well as size.
         mGlowAlphaFinish = Math.max(
                 mGlowAlphaStart, Math.min(velocity * VELOCITY_GLOW_FACTOR * .00001f, MAX_ALPHA));
+        mTargetDisplacement = 0.5f;
     }
 
 
@@ -330,52 +306,42 @@
     public boolean draw(Canvas canvas) {
         update();
 
-        mGlow.setAlpha((int) (Math.max(0, Math.min(mGlowAlpha, 1)) * 255));
+        final int count = canvas.save();
 
-        int glowBottom = (int) Math.min(
-                mGlowHeight * mGlowScaleY * mGlowHeight / mGlowWidth * 0.6f,
-                mGlowHeight * MAX_GLOW_HEIGHT);
-        if (mWidth < mMinWidth) {
-            // Center the glow and clip it.
-            int glowLeft = (mWidth - mMinWidth)/2;
-            mGlow.setBounds(glowLeft, 0, mWidth - glowLeft, glowBottom);
-        } else {
-            // Stretch the glow to fit.
-            mGlow.setBounds(0, 0, mWidth, glowBottom);
+        final float y = mBounds.height();
+        final float centerY = y - mRadius;
+        final float centerX = mBounds.centerX();
+        mArcRect.set(centerX - mRadius, centerY - mRadius, centerX + mRadius, centerY + mRadius);
+        canvas.scale(1.f, Math.min(mGlowScaleY, 1.f), centerX, 0);
+
+        final float displacement = Math.max(0, Math.min(mDisplacement, 1.f)) - 0.5f;
+        float translateX = mBounds.width() * displacement;
+        float translateY = 0;
+        if (mGlowScaleY > 1.f) {
+            translateY = (mGlowScaleY - 1.f) * mBounds.height();
         }
+        canvas.clipRect(Float.MIN_VALUE, mBounds.top,
+                Float.MAX_VALUE, Float.MAX_VALUE);
+        canvas.translate(translateX, translateY);
+        canvas.drawArc(mArcRect, 0, 180, true, mPaint);
+        canvas.restoreToCount(count);
 
-        mGlow.draw(canvas);
-
-        mEdge.setAlpha((int) (Math.max(0, Math.min(mEdgeAlpha, 1)) * 255));
-
-        int edgeBottom = (int) (mEdgeHeight * mEdgeScaleY);
-        if (mWidth < mMinWidth) {
-            // Center the edge and clip it.
-            int edgeLeft = (mWidth - mMinWidth)/2;
-            mEdge.setBounds(edgeLeft, 0, mWidth - edgeLeft, edgeBottom);
-        } else {
-            // Stretch the edge to fit.
-            mEdge.setBounds(0, 0, mWidth, edgeBottom);
-        }
-        mEdge.draw(canvas);
-
-        if (mState == STATE_RECEDE && glowBottom == 0 && edgeBottom == 0) {
+        boolean oneLastFrame = false;
+        if (mState == STATE_RECEDE && mGlowScaleY == 0) {
             mState = STATE_IDLE;
+            oneLastFrame = true;
         }
 
-        return mState != STATE_IDLE;
+        return mState != STATE_IDLE || oneLastFrame;
     }
 
     /**
-     * Returns the bounds of the edge effect.
-     * 
-     * @hide
+     * Return the maximum height that the edge effect will be drawn at given the original
+     * {@link #setSize(int, int) input size}.
+     * @return The maximum height of the edge effect
      */
-    public Rect getBounds(boolean reverse) {
-        mBounds.set(0, 0, mWidth, mMaxEffectHeight);
-        mBounds.offset(mX, mY - (reverse ? mMaxEffectHeight : 0));
-
-        return mBounds;
+    public int getMaxHeight() {
+        return (int) (mBounds.height() * MAX_GLOW_HEIGHT + 0.5f);
     }
 
     private void update() {
@@ -384,10 +350,9 @@
 
         final float interp = mInterpolator.getInterpolation(t);
 
-        mEdgeAlpha = mEdgeAlphaStart + (mEdgeAlphaFinish - mEdgeAlphaStart) * interp;
-        mEdgeScaleY = mEdgeScaleYStart + (mEdgeScaleYFinish - mEdgeScaleYStart) * interp;
         mGlowAlpha = mGlowAlphaStart + (mGlowAlphaFinish - mGlowAlphaStart) * interp;
         mGlowScaleY = mGlowScaleYStart + (mGlowScaleYFinish - mGlowScaleYStart) * interp;
+        mDisplacement = (mDisplacement + mTargetDisplacement) / 2;
 
         if (t >= 1.f - EPSILON) {
             switch (mState) {
@@ -396,14 +361,10 @@
                     mStartTime = AnimationUtils.currentAnimationTimeMillis();
                     mDuration = RECEDE_TIME;
 
-                    mEdgeAlphaStart = mEdgeAlpha;
-                    mEdgeScaleYStart = mEdgeScaleY;
                     mGlowAlphaStart = mGlowAlpha;
                     mGlowScaleYStart = mGlowScaleY;
 
-                    // After absorb, the glow and edge should fade to nothing.
-                    mEdgeAlphaFinish = 0.f;
-                    mEdgeScaleYFinish = 0.f;
+                    // After absorb, the glow should fade to nothing.
                     mGlowAlphaFinish = 0.f;
                     mGlowScaleYFinish = 0.f;
                     break;
@@ -412,26 +373,14 @@
                     mStartTime = AnimationUtils.currentAnimationTimeMillis();
                     mDuration = PULL_DECAY_TIME;
 
-                    mEdgeAlphaStart = mEdgeAlpha;
-                    mEdgeScaleYStart = mEdgeScaleY;
                     mGlowAlphaStart = mGlowAlpha;
                     mGlowScaleYStart = mGlowScaleY;
 
-                    // After pull, the glow and edge should fade to nothing.
-                    mEdgeAlphaFinish = 0.f;
-                    mEdgeScaleYFinish = 0.f;
+                    // After pull, the glow should fade to nothing.
                     mGlowAlphaFinish = 0.f;
                     mGlowScaleYFinish = 0.f;
                     break;
                 case STATE_PULL_DECAY:
-                    // When receding, we want edge to decrease more slowly
-                    // than the glow.
-                    float factor = mGlowScaleYFinish != 0 ? 1
-                            / (mGlowScaleYFinish * mGlowScaleYFinish)
-                            : Float.MAX_VALUE;
-                    mEdgeScaleY = mEdgeScaleYStart +
-                        (mEdgeScaleYFinish - mEdgeScaleYStart) *
-                            interp * factor;
                     mState = STATE_RECEDE;
                     break;
                 case STATE_RECEDE:
diff --git a/core/java/android/widget/HorizontalScrollView.java b/core/java/android/widget/HorizontalScrollView.java
index 25d4f42..0c65c50 100644
--- a/core/java/android/widget/HorizontalScrollView.java
+++ b/core/java/android/widget/HorizontalScrollView.java
@@ -616,12 +616,14 @@
                     if (canOverscroll) {
                         final int pulledToX = oldX + deltaX;
                         if (pulledToX < 0) {
-                            mEdgeGlowLeft.onPull((float) deltaX / getWidth());
+                            mEdgeGlowLeft.onPull((float) deltaX / getWidth(),
+                                    1.f - ev.getY(activePointerIndex) / getHeight());
                             if (!mEdgeGlowRight.isFinished()) {
                                 mEdgeGlowRight.onRelease();
                             }
                         } else if (pulledToX > range) {
-                            mEdgeGlowRight.onPull((float) deltaX / getWidth());
+                            mEdgeGlowRight.onPull((float) deltaX / getWidth(),
+                                    ev.getY(activePointerIndex) / getHeight());
                             if (!mEdgeGlowLeft.isFinished()) {
                                 mEdgeGlowLeft.onRelease();
                             }
diff --git a/core/java/android/widget/ProgressBar.java b/core/java/android/widget/ProgressBar.java
index f7e81b8..0c3715d 100644
--- a/core/java/android/widget/ProgressBar.java
+++ b/core/java/android/widget/ProgressBar.java
@@ -1066,21 +1066,30 @@
     protected synchronized void onDraw(Canvas canvas) {
         super.onDraw(canvas);
 
-        Drawable d = mCurrentDrawable;
+        drawTrack(canvas);
+    }
+
+    /**
+     * Draws the progress bar track.
+     */
+    void drawTrack(Canvas canvas) {
+        final Drawable d = mCurrentDrawable;
         if (d != null) {
             // Translate canvas so a indeterminate circular progress bar with padding
             // rotates properly in its animation
-            canvas.save();
+            final int saveCount = canvas.save();
+
             if(isLayoutRtl() && mMirrorForRtl) {
                 canvas.translate(getWidth() - mPaddingRight, mPaddingTop);
                 canvas.scale(-1.0f, 1.0f);
             } else {
                 canvas.translate(mPaddingLeft, mPaddingTop);
             }
-            long time = getDrawingTime();
+
+            final long time = getDrawingTime();
             if (mHasAnimation) {
                 mAnimation.getTransformation(time, mTransformation);
-                float scale = mTransformation.getAlpha();
+                final float scale = mTransformation.getAlpha();
                 try {
                     mInDrawing = true;
                     d.setLevel((int) (scale * MAX_LEVEL));
@@ -1089,8 +1098,10 @@
                 }
                 postInvalidateOnAnimation();
             }
+
             d.draw(canvas);
-            canvas.restore();
+            canvas.restoreToCount(saveCount);
+
             if (mShouldStartAnimationDrawable && d instanceof Animatable) {
                 ((Animatable) d).start();
                 mShouldStartAnimationDrawable = false;
diff --git a/core/java/android/widget/ScrollView.java b/core/java/android/widget/ScrollView.java
index 0fa75a6..fd04890 100644
--- a/core/java/android/widget/ScrollView.java
+++ b/core/java/android/widget/ScrollView.java
@@ -669,12 +669,14 @@
                     } else if (canOverscroll) {
                         final int pulledToY = oldY + deltaY;
                         if (pulledToY < 0) {
-                            mEdgeGlowTop.onPull((float) deltaY / getHeight());
+                            mEdgeGlowTop.onPull((float) deltaY / getHeight(),
+                                    ev.getX(activePointerIndex) / getWidth());
                             if (!mEdgeGlowBottom.isFinished()) {
                                 mEdgeGlowBottom.onRelease();
                             }
                         } else if (pulledToY > range) {
-                            mEdgeGlowBottom.onPull((float) deltaY / getHeight());
+                            mEdgeGlowBottom.onPull((float) deltaY / getHeight(),
+                                    1.f - ev.getX(activePointerIndex) / getWidth());
                             if (!mEdgeGlowTop.isFinished()) {
                                 mEdgeGlowTop.onRelease();
                             }
diff --git a/core/java/android/widget/Switch.java b/core/java/android/widget/Switch.java
index 08af4de..438e164 100644
--- a/core/java/android/widget/Switch.java
+++ b/core/java/android/widget/Switch.java
@@ -22,9 +22,11 @@
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.graphics.Canvas;
+import android.graphics.Insets;
 import android.graphics.Paint;
 import android.graphics.Rect;
 import android.graphics.Typeface;
+import android.graphics.Region.Op;
 import android.graphics.drawable.Drawable;
 import android.text.Layout;
 import android.text.StaticLayout;
@@ -85,6 +87,7 @@
     private int mThumbTextPadding;
     private int mSwitchMinWidth;
     private int mSwitchPadding;
+    private boolean mSplitTrack;
     private CharSequence mTextOn;
     private CharSequence mTextOff;
 
@@ -174,13 +177,13 @@
         super(context, attrs, defStyleAttr, defStyleRes);
 
         mTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
-        Resources res = getResources();
+
+        final Resources res = getResources();
         mTextPaint.density = res.getDisplayMetrics().density;
         mTextPaint.setCompatibilityScaling(res.getCompatibilityInfo().applicationScale);
 
         final TypedArray a = context.obtainStyledAttributes(
                 attrs, com.android.internal.R.styleable.Switch, defStyleAttr, defStyleRes);
-
         mThumbDrawable = a.getDrawable(com.android.internal.R.styleable.Switch_thumb);
         mTrackDrawable = a.getDrawable(com.android.internal.R.styleable.Switch_track);
         mTextOn = a.getText(com.android.internal.R.styleable.Switch_textOn);
@@ -191,15 +194,16 @@
                 com.android.internal.R.styleable.Switch_switchMinWidth, 0);
         mSwitchPadding = a.getDimensionPixelSize(
                 com.android.internal.R.styleable.Switch_switchPadding, 0);
+        mSplitTrack = a.getBoolean(com.android.internal.R.styleable.Switch_splitTrack, false);
 
-        int appearance = a.getResourceId(
+        final int appearance = a.getResourceId(
                 com.android.internal.R.styleable.Switch_switchTextAppearance, 0);
         if (appearance != 0) {
             setSwitchTextAppearance(context, appearance);
         }
         a.recycle();
 
-        ViewConfiguration config = ViewConfiguration.get(context);
+        final ViewConfiguration config = ViewConfiguration.get(context);
         mTouchSlop = config.getScaledTouchSlop();
         mMinFlingVelocity = config.getScaledMinimumFlingVelocity();
 
@@ -469,6 +473,29 @@
     }
 
     /**
+     * Specifies whether the track should be split by the thumb. When true,
+     * the thumb's optical bounds will be clipped out of the track drawable,
+     * then the thumb will be drawn into the resulting gap.
+     *
+     * @param splitTrack Whether the track should be split by the thumb
+     *
+     * @attr ref android.R.styleable#Switch_splitTrack
+     */
+    public void setSplitTrack(boolean splitTrack) {
+        mSplitTrack = splitTrack;
+        invalidate();
+    }
+
+    /**
+     * Returns whether the track should be split by the thumb.
+     *
+     * @attr ref android.R.styleable#Switch_splitTrack
+     */
+    public boolean getSplitTrack() {
+        return mSplitTrack;
+    }
+
+    /**
      * Returns the text displayed when the button is in the checked state.
      *
      * @attr ref android.R.styleable#Switch_textOn
@@ -518,13 +545,15 @@
 
         mTrackDrawable.getPadding(mTempRect);
 
-        final int maxTextWidth = Math.max(mOnLayout.getWidth(), mOffLayout.getWidth());
+        final int maxTextWidth = Math.max(mOnLayout.getWidth(), mOffLayout.getWidth())
+                + mThumbTextPadding * 2;
+        mThumbWidth = Math.max(maxTextWidth, mThumbDrawable.getIntrinsicWidth());
+
         final int switchWidth = Math.max(mSwitchMinWidth,
-                maxTextWidth * 2 + mThumbTextPadding * 4 + mTempRect.left + mTempRect.right);
+                2 * mThumbWidth + mTempRect.left + mTempRect.right);
         final int switchHeight = Math.max(mTrackDrawable.getIntrinsicHeight(),
                 mThumbDrawable.getIntrinsicHeight());
 
-        mThumbWidth = maxTextWidth + mThumbTextPadding * 2;
 
         mSwitchWidth = switchWidth;
         mSwitchHeight = switchHeight;
@@ -777,7 +806,7 @@
         final Drawable trackDrawable = mTrackDrawable;
         final Drawable thumbDrawable = mThumbDrawable;
 
-        // Draw the switch
+        // Layout the track.
         final int switchLeft = mSwitchLeft;
         final int switchTop = mSwitchTop;
         final int switchRight = mSwitchRight;
@@ -793,9 +822,10 @@
         // Relies on mTempRect, MUST be called first!
         final int thumbPos = getThumbOffset();
 
+        // Layout the thumb.
         thumbDrawable.getPadding(tempRect);
-        int thumbLeft = switchInnerLeft - tempRect.left + thumbPos;
-        int thumbRight = switchInnerLeft + thumbPos + mThumbWidth + tempRect.right;
+        final int thumbLeft = switchInnerLeft - tempRect.left + thumbPos;
+        final int thumbRight = switchInnerLeft + thumbPos + mThumbWidth + tempRect.right;
         thumbDrawable.setBounds(thumbLeft, switchTop, thumbRight, switchBottom);
 
         final Drawable background = getBackground();
@@ -805,20 +835,32 @@
 
         super.onDraw(canvas);
 
-        trackDrawable.draw(canvas);
+        if (mSplitTrack) {
+            final Insets insets = thumbDrawable.getOpticalInsets();
+            thumbDrawable.copyBounds(tempRect);
+            tempRect.left += insets.left;
+            tempRect.right -= insets.right;
+
+            final int saveCount = canvas.save();
+            canvas.clipRect(tempRect, Op.DIFFERENCE);
+            trackDrawable.draw(canvas);
+            canvas.restoreToCount(saveCount);
+        } else {
+            trackDrawable.draw(canvas);
+        }
 
         final int saveCount = canvas.save();
         canvas.clipRect(switchInnerLeft, switchTop, switchInnerRight, switchBottom);
         thumbDrawable.draw(canvas);
 
-        final int drawableState[] = getDrawableState();
-        if (mTextColors != null) {
-            mTextPaint.setColor(mTextColors.getColorForState(drawableState, 0));
-        }
-        mTextPaint.drawableState = drawableState;
-
         final Layout switchText = getTargetCheckedState() ? mOnLayout : mOffLayout;
         if (switchText != null) {
+            final int drawableState[] = getDrawableState();
+            if (mTextColors != null) {
+                mTextPaint.setColor(mTextColors.getColorForState(drawableState, 0));
+            }
+            mTextPaint.drawableState = drawableState;
+
             final int left = (thumbLeft + thumbRight) / 2 - switchText.getWidth() / 2;
             final int top = (switchInnerTop + switchInnerBottom) / 2 - switchText.getHeight() / 2;
             canvas.translate(left, top);
@@ -889,12 +931,16 @@
     protected void drawableStateChanged() {
         super.drawableStateChanged();
 
-        int[] myDrawableState = getDrawableState();
+        final int[] myDrawableState = getDrawableState();
 
-        // Set the state of the Drawable
-        // Drawable may be null when checked state is set from XML, from super constructor
-        if (mThumbDrawable != null) mThumbDrawable.setState(myDrawableState);
-        if (mTrackDrawable != null) mTrackDrawable.setState(myDrawableState);
+        if (mThumbDrawable != null && mThumbDrawable.setState(myDrawableState)) {
+            // Handle changes to thumb width and height.
+            requestLayout();
+        }
+
+        if (mTrackDrawable != null) {
+            mTrackDrawable.setState(myDrawableState);
+        }
 
         invalidate();
     }
diff --git a/core/java/com/android/internal/app/AlertController.java b/core/java/com/android/internal/app/AlertController.java
index 4726da7..b568121 100644
--- a/core/java/com/android/internal/app/AlertController.java
+++ b/core/java/com/android/internal/app/AlertController.java
@@ -26,6 +26,7 @@
 import android.content.res.TypedArray;
 import android.database.Cursor;
 import android.graphics.drawable.Drawable;
+import android.os.Build;
 import android.os.Handler;
 import android.os.Message;
 import android.text.TextUtils;
@@ -38,6 +39,7 @@
 import android.view.ViewGroup;
 import android.view.ViewGroup.LayoutParams;
 import android.view.Window;
+import android.view.WindowInsets;
 import android.view.WindowManager;
 import android.widget.AdapterView;
 import android.widget.AdapterView.OnItemClickListener;
@@ -240,6 +242,7 @@
         mWindow.requestFeature(Window.FEATURE_NO_TITLE);
         mWindow.setContentView(mAlertDialogLayout);
         setupView();
+        setupDecor();
     }
     
     public void setTitle(CharSequence title) {
@@ -415,7 +418,28 @@
     public boolean onKeyUp(int keyCode, KeyEvent event) {
         return mScrollView != null && mScrollView.executeKeyEvent(event);
     }
-    
+
+    private void setupDecor() {
+        final View decor = mWindow.getDecorView();
+        final View parent = mWindow.findViewById(R.id.parentPanel);
+        if (parent != null && decor != null) {
+            decor.setOnApplyWindowInsetsListener(new View.OnApplyWindowInsetsListener() {
+                @Override
+                public WindowInsets onApplyWindowInsets(View view, WindowInsets insets) {
+                    if (insets.isRound()) {
+                        // TODO: Get the padding as a function of the window size.
+                        int roundOffset = mContext.getResources().getDimensionPixelOffset(
+                                R.dimen.alert_dialog_round_padding);
+                        parent.setPadding(roundOffset, roundOffset, roundOffset, roundOffset);
+                    }
+                    return insets.consumeSystemWindowInsets();
+                }
+            });
+            decor.setFitsSystemWindows(true);
+            decor.requestApplyInsets();
+        }
+    }
+
     private void setupView() {
         LinearLayout contentPanel = (LinearLayout) mWindow.findViewById(R.id.contentPanel);
         setupContent(contentPanel);
@@ -636,14 +660,31 @@
 
     private void setBackground(TypedArray a, View topPanel, View contentPanel, View customPanel,
             View buttonPanel, boolean hasTitle, boolean hasCustomView, boolean hasButtons) {
-        final int topBright = a.getResourceId(
-                R.styleable.AlertDialog_topBright, R.drawable.popup_top_bright);
-        final int topDark = a.getResourceId(
-                R.styleable.AlertDialog_topDark, R.drawable.popup_top_dark);
-        final int centerBright = a.getResourceId(
-                R.styleable.AlertDialog_centerBright, R.drawable.popup_center_bright);
-        final int centerDark = a.getResourceId(
-                R.styleable.AlertDialog_centerDark, R.drawable.popup_center_dark);
+        int fullDark = 0;
+        int topDark = 0;
+        int centerDark = 0;
+        int bottomDark = 0;
+        int fullBright = 0;
+        int topBright = 0;
+        int centerBright = 0;
+        int bottomBright = 0;
+        int bottomMedium = 0;
+        if (mContext.getApplicationInfo().targetSdkVersion <= Build.VERSION_CODES.KITKAT) {
+            fullDark = R.drawable.popup_full_dark;
+            topDark = R.drawable.popup_top_dark;
+            centerDark = R.drawable.popup_center_dark;
+            bottomDark = R.drawable.popup_bottom_dark;
+            fullBright = R.drawable.popup_full_bright;
+            topBright = R.drawable.popup_top_bright;
+            centerBright = R.drawable.popup_center_bright;
+            bottomBright = R.drawable.popup_bottom_bright;
+            bottomMedium = R.drawable.popup_bottom_medium;
+        }
+        topBright = a.getResourceId(R.styleable.AlertDialog_topBright, topBright);
+        topDark = a.getResourceId(R.styleable.AlertDialog_topDark, topDark);
+        centerBright = a.getResourceId(R.styleable.AlertDialog_centerBright, centerBright);
+        centerDark = a.getResourceId(R.styleable.AlertDialog_centerDark, centerDark);
+
 
         /* We now set the background of all of the sections of the alert.
          * First collect together each section that is being displayed along
@@ -707,22 +748,17 @@
 
         if (lastView != null) {
             if (setView) {
-                final int bottomBright = a.getResourceId(
-                        R.styleable.AlertDialog_bottomBright, R.drawable.popup_bottom_bright);
-                final int bottomMedium = a.getResourceId(
-                        R.styleable.AlertDialog_bottomMedium, R.drawable.popup_bottom_medium);
-                final int bottomDark = a.getResourceId(
-                        R.styleable.AlertDialog_bottomDark, R.drawable.popup_bottom_dark);
+                bottomBright = a.getResourceId(R.styleable.AlertDialog_bottomBright, bottomBright);
+                bottomMedium = a.getResourceId(R.styleable.AlertDialog_bottomMedium, bottomMedium);
+                bottomDark = a.getResourceId(R.styleable.AlertDialog_bottomDark, bottomDark);
 
                 // ListViews will use the Bright background, but buttons use the
                 // Medium background.
                 lastView.setBackgroundResource(
                         lastLight ? (hasButtons ? bottomMedium : bottomBright) : bottomDark);
             } else {
-                final int fullBright = a.getResourceId(
-                        R.styleable.AlertDialog_fullBright, R.drawable.popup_full_bright);
-                final int fullDark = a.getResourceId(
-                        R.styleable.AlertDialog_fullDark, R.drawable.popup_full_dark);
+                fullBright = a.getResourceId(R.styleable.AlertDialog_fullBright, fullBright);
+                fullDark = a.getResourceId(R.styleable.AlertDialog_fullDark, fullDark);
 
                 lastView.setBackgroundResource(lastLight ? fullBright : fullDark);
             }
diff --git a/core/java/com/android/internal/app/ProcessStats.java b/core/java/com/android/internal/app/ProcessStats.java
index 882bec9..41f3337 100644
--- a/core/java/com/android/internal/app/ProcessStats.java
+++ b/core/java/com/android/internal/app/ProcessStats.java
@@ -1108,13 +1108,6 @@
                 mRuntime = runtime;
             }
         }
-        String webview = WebViewFactory.useExperimentalWebView() ? "chromeview" : "webview";
-        if (!Objects.equals(webview, mWebView)) {
-            changed = true;
-            if (update) {
-                mWebView = webview;
-            }
-        }
         return changed;
     }
 
diff --git a/core/java/com/android/internal/notification/DemoContactNotificationScorer.java b/core/java/com/android/internal/notification/DemoContactNotificationScorer.java
deleted file mode 100644
index f484724..0000000
--- a/core/java/com/android/internal/notification/DemoContactNotificationScorer.java
+++ /dev/null
@@ -1,188 +0,0 @@
-/*
-* Copyright (C) 2013 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*      http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
-
-package com.android.internal.notification;
-
-import android.app.Notification;
-import android.content.Context;
-import android.database.Cursor;
-import android.net.Uri;
-import android.os.Bundle;
-import android.provider.ContactsContract;
-import android.provider.Settings;
-import android.text.SpannableString;
-import android.util.Slog;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * This NotificationScorer bumps up the priority of notifications that contain references to the
- * display names of starred contacts. The references it picks up are spannable strings which, in
- * their entirety, match the display name of some starred contact. The magnitude of the bump ranges
- * from 0 to 15 (assuming NOTIFICATION_PRIORITY_MULTIPLIER = 10) depending on the initial score, and
- * the mapping is defined by priorityBumpMap. In a production version of this scorer, a notification
- * extra will be used to specify contact identifiers.
- */
-
-public class DemoContactNotificationScorer implements NotificationScorer {
-    private static final String TAG = "DemoContactNotificationScorer";
-    private static final boolean DBG = false;
-
-    protected static final boolean ENABLE_CONTACT_SCORER = true;
-    private static final String SETTING_ENABLE_SCORER = "contact_scorer_enabled";
-    protected boolean mEnabled;
-
-    // see NotificationManagerService
-    private static final int NOTIFICATION_PRIORITY_MULTIPLIER = 10;
-
-    private Context mContext;
-
-    private static final List<String> RELEVANT_KEYS_LIST = Arrays.asList(
-            Notification.EXTRA_INFO_TEXT, Notification.EXTRA_TEXT, Notification.EXTRA_TEXT_LINES,
-            Notification.EXTRA_SUB_TEXT, Notification.EXTRA_TITLE
-    );
-
-    private static final String[] PROJECTION = new String[] {
-            ContactsContract.Contacts._ID, ContactsContract.Contacts.DISPLAY_NAME
-    };
-
-    private static final Uri CONTACTS_URI = ContactsContract.Contacts.CONTENT_URI;
-
-    private static List<String> extractSpannedStrings(CharSequence charSequence) {
-        if (charSequence == null) return Collections.emptyList();
-        if (!(charSequence instanceof SpannableString)) {
-            return Arrays.asList(charSequence.toString());
-        }
-        SpannableString spannableString = (SpannableString)charSequence;
-        // get all spans
-        Object[] ssArr = spannableString.getSpans(0, spannableString.length(), Object.class);
-        // spanned string sequences
-        ArrayList<String> sss = new ArrayList<String>();
-        for (Object spanObj : ssArr) {
-            try {
-                sss.add(spannableString.subSequence(spannableString.getSpanStart(spanObj),
-                        spannableString.getSpanEnd(spanObj)).toString());
-            } catch(StringIndexOutOfBoundsException e) {
-                Slog.e(TAG, "Bad indices when extracting spanned subsequence", e);
-            }
-        }
-        return sss;
-    };
-
-    private static String getQuestionMarksInParens(int n) {
-        StringBuilder sb = new StringBuilder("(");
-        for (int i = 0; i < n; i++) {
-            if (sb.length() > 1) sb.append(',');
-            sb.append('?');
-        }
-        sb.append(")");
-        return sb.toString();
-    }
-
-    private boolean hasStarredContact(Bundle extras) {
-        if (extras == null) return false;
-        ArrayList<String> qStrings = new ArrayList<String>();
-        // build list to query against the database for display names.
-        for (String rk: RELEVANT_KEYS_LIST) {
-            if (extras.get(rk) == null) {
-                continue;
-            } else if (extras.get(rk) instanceof CharSequence) {
-                qStrings.addAll(extractSpannedStrings((CharSequence) extras.get(rk)));
-            } else if (extras.get(rk) instanceof CharSequence[]) {
-                // this is intended for Notification.EXTRA_TEXT_LINES
-                for (CharSequence line: (CharSequence[]) extras.get(rk)){
-                    qStrings.addAll(extractSpannedStrings(line));
-                }
-            } else {
-                Slog.w(TAG, "Strange, the extra " + rk + " is of unexpected type.");
-            }
-        }
-        if (qStrings.isEmpty()) return false;
-        String[] qStringsArr = qStrings.toArray(new String[qStrings.size()]);
-
-        String selection = ContactsContract.Contacts.DISPLAY_NAME + " IN "
-                + getQuestionMarksInParens(qStringsArr.length) + " AND "
-                + ContactsContract.Contacts.STARRED+" ='1'";
-
-        Cursor c = null;
-        try {
-            c = mContext.getContentResolver().query(
-                    CONTACTS_URI, PROJECTION, selection, qStringsArr, null);
-            if (c != null) return c.getCount() > 0;
-        } catch(Throwable t) {
-            Slog.w(TAG, "Problem getting content resolver or performing contacts query.", t);
-        } finally {
-            if (c != null) {
-                c.close();
-            }
-        }
-        return false;
-    }
-
-    private final static int clamp(int x, int low, int high) {
-        return (x < low) ? low : ((x > high) ? high : x);
-    }
-
-    private static int priorityBumpMap(int incomingScore) {
-        //assumption is that scale runs from [-2*pm, 2*pm]
-        int pm = NOTIFICATION_PRIORITY_MULTIPLIER;
-        int theScore = incomingScore;
-        // enforce input in range
-        theScore = clamp(theScore, -2 * pm, 2 * pm);
-        if (theScore != incomingScore) return incomingScore;
-        // map -20 -> -20 and -10 -> 5 (when pm = 10)
-        if (theScore <= -pm) {
-            theScore += 1.5 * (theScore + 2 * pm);
-        } else {
-            // map 0 -> 10, 10 -> 15, 20 -> 20;
-            theScore += 0.5 * (2 * pm - theScore);
-        }
-        if (DBG) Slog.v(TAG, "priorityBumpMap: score before: " + incomingScore
-                + ", score after " + theScore + ".");
-        return theScore;
-    }
-
-    @Override
-    public void initialize(Context context) {
-        if (DBG) Slog.v(TAG, "Initializing  " + getClass().getSimpleName() + ".");
-        mContext = context;
-        mEnabled = ENABLE_CONTACT_SCORER && 1 == Settings.Global.getInt(
-                mContext.getContentResolver(), SETTING_ENABLE_SCORER, 0);
-    }
-
-    @Override
-    public int getScore(Notification notification, int score) {
-        if (notification == null || !mEnabled) {
-            if (DBG) Slog.w(TAG, "empty notification? scorer disabled?");
-            return score;
-        }
-        boolean hasStarredPriority = hasStarredContact(notification.extras);
-
-        if (DBG) {
-            if (hasStarredPriority) {
-                Slog.v(TAG, "Notification references starred contact. Promoted!");
-            } else {
-                Slog.v(TAG, "Notification lacks any starred contact reference. Not promoted!");
-            }
-        }
-        if (hasStarredPriority) score = priorityBumpMap(score);
-        return score;
-    }
-}
-
diff --git a/core/java/com/android/internal/notification/NotificationScorer.java b/core/java/com/android/internal/notification/NotificationScorer.java
deleted file mode 100644
index 863c08c..0000000
--- a/core/java/com/android/internal/notification/NotificationScorer.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
-* Copyright (C) 2013 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*      http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
-
-package com.android.internal.notification;
-
-import android.app.Notification;
-import android.content.Context;
-
-public interface NotificationScorer {
-
-    public void initialize(Context context);
-    public int getScore(Notification notification, int score);
-
-}
diff --git a/core/java/com/android/internal/notification/PeopleNotificationScorer.java b/core/java/com/android/internal/notification/PeopleNotificationScorer.java
deleted file mode 100644
index efb5f63..0000000
--- a/core/java/com/android/internal/notification/PeopleNotificationScorer.java
+++ /dev/null
@@ -1,227 +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.internal.notification;
-
-import android.app.Notification;
-import android.content.Context;
-import android.database.Cursor;
-import android.net.Uri;
-import android.os.Bundle;
-import android.provider.ContactsContract;
-import android.provider.ContactsContract.Contacts;
-import android.provider.Settings;
-import android.text.TextUtils;
-import android.util.LruCache;
-import android.util.Slog;
-
-/**
- * This {@link NotificationScorer} attempts to validate people references.
- * Also elevates the priority of real people.
- */
-public class PeopleNotificationScorer implements NotificationScorer {
-    private static final String TAG = "PeopleNotificationScorer";
-    private static final boolean DBG = false;
-
-    private static final boolean ENABLE_PEOPLE_SCORER = true;
-    private static final String SETTING_ENABLE_PEOPLE_SCORER = "people_scorer_enabled";
-    private static final String[] LOOKUP_PROJECTION = { Contacts._ID };
-    private static final int MAX_PEOPLE = 10;
-    private static final int PEOPLE_CACHE_SIZE = 200;
-    // see NotificationManagerService
-    private static final int NOTIFICATION_PRIORITY_MULTIPLIER = 10;
-
-    protected boolean mEnabled;
-    private Context mContext;
-
-    // maps raw person handle to resolved person object
-    private LruCache<String, LookupResult> mPeopleCache;
-
-    private float findMaxContactScore(Bundle extras) {
-        if (extras == null) {
-            return 0f;
-        }
-
-        final String[] people = extras.getStringArray(Notification.EXTRA_PEOPLE);
-        if (people == null || people.length == 0) {
-            return 0f;
-        }
-
-        float rank = 0f;
-        for (int personIdx = 0; personIdx < people.length && personIdx < MAX_PEOPLE; personIdx++) {
-            final String handle = people[personIdx];
-            if (TextUtils.isEmpty(handle)) continue;
-
-            LookupResult lookupResult = mPeopleCache.get(handle);
-            if (lookupResult == null || lookupResult.isExpired()) {
-                final Uri uri = Uri.parse(handle);
-                if ("tel".equals(uri.getScheme())) {
-                    if (DBG) Slog.w(TAG, "checking telephone URI: " + handle);
-                    lookupResult = lookupPhoneContact(handle, uri.getSchemeSpecificPart());
-                } else if (handle.startsWith(Contacts.CONTENT_LOOKUP_URI.toString())) {
-                    if (DBG) Slog.w(TAG, "checking lookup URI: " + handle);
-                    lookupResult = resolveContactsUri(handle, uri);
-                } else {
-                    if (DBG) Slog.w(TAG, "unsupported URI " + handle);
-                }
-            } else {
-                if (DBG) Slog.w(TAG, "using cached lookupResult: " + lookupResult.mId);
-            }
-            if (lookupResult != null) {
-                rank = Math.max(rank, lookupResult.getRank());
-            }
-        }
-        return rank;
-    }
-
-    private LookupResult lookupPhoneContact(final String handle, final String number) {
-        LookupResult lookupResult = null;
-        Cursor c = null;
-        try {
-            Uri numberUri = Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI,
-                    Uri.encode(number));
-            c = mContext.getContentResolver().query(numberUri, LOOKUP_PROJECTION, null, null, null);
-            if (c != null && c.getCount() > 0) {
-                c.moveToFirst();
-                final int idIdx = c.getColumnIndex(Contacts._ID);
-                final int id = c.getInt(idIdx);
-                if (DBG) Slog.w(TAG, "is valid: " + id);
-                lookupResult = new LookupResult(id);
-            }
-        } catch(Throwable t) {
-            Slog.w(TAG, "Problem getting content resolver or performing contacts query.", t);
-        } finally {
-            if (c != null) {
-                c.close();
-            }
-        }
-        if (lookupResult == null) {
-            lookupResult = new LookupResult(LookupResult.INVALID_ID);
-        }
-        mPeopleCache.put(handle, lookupResult);
-        return lookupResult;
-    }
-
-    private LookupResult resolveContactsUri(String handle, final Uri personUri) {
-        LookupResult lookupResult = null;
-        Cursor c = null;
-        try {
-            c = mContext.getContentResolver().query(personUri, LOOKUP_PROJECTION, null, null, null);
-            if (c != null && c.getCount() > 0) {
-                c.moveToFirst();
-                final int idIdx = c.getColumnIndex(Contacts._ID);
-                final int id = c.getInt(idIdx);
-                if (DBG) Slog.w(TAG, "is valid: " + id);
-                lookupResult = new LookupResult(id);
-            }
-        } catch(Throwable t) {
-            Slog.w(TAG, "Problem getting content resolver or performing contacts query.", t);
-        } finally {
-            if (c != null) {
-                c.close();
-            }
-        }
-        if (lookupResult == null) {
-            lookupResult = new LookupResult(LookupResult.INVALID_ID);
-        }
-        mPeopleCache.put(handle, lookupResult);
-        return lookupResult;
-    }
-
-    private final static int clamp(int x, int low, int high) {
-        return (x < low) ? low : ((x > high) ? high : x);
-    }
-
-    // TODO: rework this function before shipping
-    private static int priorityBumpMap(int incomingScore) {
-        //assumption is that scale runs from [-2*pm, 2*pm]
-        int pm = NOTIFICATION_PRIORITY_MULTIPLIER;
-        int theScore = incomingScore;
-        // enforce input in range
-        theScore = clamp(theScore, -2 * pm, 2 * pm);
-        if (theScore != incomingScore) return incomingScore;
-        // map -20 -> -20 and -10 -> 5 (when pm = 10)
-        if (theScore <= -pm) {
-            theScore += 1.5 * (theScore + 2 * pm);
-        } else {
-            // map 0 -> 10, 10 -> 15, 20 -> 20;
-            theScore += 0.5 * (2 * pm - theScore);
-        }
-        if (DBG) Slog.v(TAG, "priorityBumpMap: score before: " + incomingScore
-                + ", score after " + theScore + ".");
-        return theScore;
-    }
-
-    @Override
-    public void initialize(Context context) {
-        if (DBG) Slog.v(TAG, "Initializing  " + getClass().getSimpleName() + ".");
-        mContext = context;
-        mPeopleCache = new LruCache<String, LookupResult>(PEOPLE_CACHE_SIZE);
-        mEnabled = ENABLE_PEOPLE_SCORER && 1 == Settings.Global.getInt(
-                mContext.getContentResolver(), SETTING_ENABLE_PEOPLE_SCORER, 0);
-    }
-
-    @Override
-    public int getScore(Notification notification, int score) {
-        if (notification == null || !mEnabled) {
-            if (DBG) Slog.w(TAG, "empty notification? scorer disabled?");
-            return score;
-        }
-        float contactScore = findMaxContactScore(notification.extras);
-        if (contactScore > 0f) {
-            if (DBG) Slog.v(TAG, "Notification references a real contact. Promoted!");
-            score = priorityBumpMap(score);
-        } else {
-            if (DBG) Slog.v(TAG, "Notification lacks any valid contact reference. Not promoted!");
-        }
-        return score;
-    }
-
-    private static class LookupResult {
-        private static final long CONTACT_REFRESH_MILLIS = 60 * 60 * 1000;  // 1hr
-        public static final int INVALID_ID = -1;
-
-        private final long mExpireMillis;
-        private int mId;
-
-        public LookupResult(int id) {
-            mId = id;
-            mExpireMillis = System.currentTimeMillis() + CONTACT_REFRESH_MILLIS;
-        }
-
-        public boolean isExpired() {
-            return mExpireMillis < System.currentTimeMillis();
-        }
-
-        public boolean isInvalid() {
-            return mId == INVALID_ID || isExpired();
-        }
-
-        public float getRank() {
-            if (isInvalid()) {
-                return 0f;
-            } else {
-                return 1f;  // TODO: finer grained score
-            }
-        }
-
-        public LookupResult setId(int id) {
-            mId = id;
-            return this;
-        }
-    }
-}
-
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 1aff190..7bd5b12 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -188,8 +188,7 @@
 
     boolean mShuttingDown;
 
-    HashMap<String, SparseBooleanArray>[] mActiveEvents
-            = (HashMap<String, SparseBooleanArray>[]) new HashMap[HistoryItem.EVENT_COUNT];
+    final HistoryEventTracker mActiveEvents = new HistoryEventTracker();
 
     long mHistoryBaseTime;
     boolean mHaveBatteryLevel = false;
@@ -2297,44 +2296,8 @@
 
     public void noteEventLocked(int code, String name, int uid) {
         uid = mapUid(uid);
-        if ((code&HistoryItem.EVENT_FLAG_START) != 0) {
-            int idx = code&~HistoryItem.EVENT_FLAG_START;
-            HashMap<String, SparseBooleanArray> active = mActiveEvents[idx];
-            if (active == null) {
-                active = new HashMap<String, SparseBooleanArray>();
-                mActiveEvents[idx] = active;
-            }
-            SparseBooleanArray uids = active.get(name);
-            if (uids == null) {
-                uids = new SparseBooleanArray();
-                active.put(name, uids);
-            }
-            if (uids.get(uid)) {
-                // Already set, nothing to do!
-                return;
-            }
-            uids.put(uid, true);
-        } else if ((code&HistoryItem.EVENT_FLAG_FINISH) != 0) {
-            int idx = code&~HistoryItem.EVENT_FLAG_FINISH;
-            HashMap<String, SparseBooleanArray> active = mActiveEvents[idx];
-            if (active == null) {
-                // not currently active, nothing to do.
-                return;
-            }
-            SparseBooleanArray uids = active.get(name);
-            if (uids == null) {
-                // not currently active, nothing to do.
-                return;
-            }
-            idx = uids.indexOfKey(uid);
-            if (idx < 0 || !uids.valueAt(idx)) {
-                // not currently active, nothing to do.
-                return;
-            }
-            uids.removeAt(idx);
-            if (uids.size() <= 0) {
-                active.remove(name);
-            }
+        if (!mActiveEvents.updateState(code, name, uid, 0)) {
+            return;
         }
         final long elapsedRealtime = SystemClock.elapsedRealtime();
         final long uptime = SystemClock.uptimeMillis();
@@ -2348,6 +2311,9 @@
         }
     }
 
+    private String mInitialAcquireWakeName;
+    private int mInitialAcquireWakeUid = -1;
+
     public void noteStartWakeLocked(int uid, int pid, String name, String historyName, int type,
             boolean unimportantForLogging, long elapsedRealtime, long uptime) {
         uid = mapUid(uid);
@@ -2360,8 +2326,9 @@
                 if (DEBUG_HISTORY) Slog.v(TAG, "Start wake lock to: "
                         + Integer.toHexString(mHistoryCur.states));
                 mHistoryCur.wakelockTag = mHistoryCur.localWakelockTag;
-                mHistoryCur.wakelockTag.string = historyName != null ? historyName : name;
-                mHistoryCur.wakelockTag.uid = uid;
+                mHistoryCur.wakelockTag.string = mInitialAcquireWakeName
+                        = historyName != null ? historyName : name;
+                mHistoryCur.wakelockTag.uid = mInitialAcquireWakeUid = uid;
                 mWakeLockImportant = !unimportantForLogging;
                 addHistoryRecordLocked(elapsedRealtime, uptime);
             } else if (!mWakeLockImportant && !unimportantForLogging) {
@@ -2369,8 +2336,9 @@
                     // We'll try to update the last tag.
                     mHistoryLastWritten.wakelockTag = null;
                     mHistoryCur.wakelockTag = mHistoryCur.localWakelockTag;
-                    mHistoryCur.wakelockTag.string = historyName != null ? historyName : name;
-                    mHistoryCur.wakelockTag.uid = uid;
+                    mHistoryCur.wakelockTag.string = mInitialAcquireWakeName
+                            = historyName != null ? historyName : name;
+                    mHistoryCur.wakelockTag.uid = mInitialAcquireWakeUid = uid;
                     addHistoryRecordLocked(elapsedRealtime, uptime);
                 }
                 mWakeLockImportant = true;
@@ -2395,6 +2363,14 @@
                 mHistoryCur.states &= ~HistoryItem.STATE_WAKE_LOCK_FLAG;
                 if (DEBUG_HISTORY) Slog.v(TAG, "Stop wake lock to: "
                         + Integer.toHexString(mHistoryCur.states));
+                if ((name != null && !name.equals(mInitialAcquireWakeName))
+                        || uid != mInitialAcquireWakeUid) {
+                    mHistoryCur.wakelockTag = mHistoryCur.localWakelockTag;
+                    mHistoryCur.wakelockTag.string = name;
+                    mHistoryCur.wakelockTag.uid = uid;
+                }
+                mInitialAcquireWakeName = null;
+                mInitialAcquireWakeUid = -1;
                 addHistoryRecordLocked(elapsedRealtime, uptime);
             }
         }
@@ -5699,7 +5675,8 @@
         final long lastRealtime = out.time;
         final long lastWalltime = out.currentTime;
         readHistoryDelta(mHistoryBuffer, out);
-        if (out.cmd != HistoryItem.CMD_CURRENT_TIME && lastWalltime != 0) {
+        if (out.cmd != HistoryItem.CMD_CURRENT_TIME
+                && out.cmd != HistoryItem.CMD_RESET && lastWalltime != 0) {
             out.currentTime = lastWalltime + (out.time - lastRealtime);
         }
         return true;
@@ -5845,17 +5822,15 @@
 
     private void initActiveHistoryEventsLocked(long elapsedRealtimeMs, long uptimeMs) {
         for (int i=0; i<HistoryItem.EVENT_COUNT; i++) {
-            HashMap<String, SparseBooleanArray> active = mActiveEvents[i];
+            HashMap<String, SparseIntArray> active = mActiveEvents.getStateForEvent(i);
             if (active == null) {
                 continue;
             }
-            for (HashMap.Entry<String, SparseBooleanArray> ent : active.entrySet()) {
-                SparseBooleanArray uids = ent.getValue();
+            for (HashMap.Entry<String, SparseIntArray> ent : active.entrySet()) {
+                SparseIntArray uids = ent.getValue();
                 for (int j=0; j<uids.size(); j++) {
-                    if (uids.valueAt(j)) {
-                        addHistoryEventLocked(elapsedRealtimeMs, uptimeMs, i, ent.getKey(),
-                                uids.keyAt(j));
-                    }
+                    addHistoryEventLocked(elapsedRealtimeMs, uptimeMs, i, ent.getKey(),
+                            uids.keyAt(j));
                 }
             }
         }
@@ -5971,7 +5946,8 @@
             boolean reset) {
         mRecordingHistory = true;
         mHistoryCur.currentTime = System.currentTimeMillis();
-        addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.CMD_CURRENT_TIME,
+        addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs,
+                reset ? HistoryItem.CMD_RESET : HistoryItem.CMD_CURRENT_TIME,
                 mHistoryCur);
         mHistoryCur.currentTime = 0;
         if (reset) {
diff --git a/core/java/com/android/internal/util/AsyncChannel.java b/core/java/com/android/internal/util/AsyncChannel.java
index 52281d9..34f62ba 100644
--- a/core/java/com/android/internal/util/AsyncChannel.java
+++ b/core/java/com/android/internal/util/AsyncChannel.java
@@ -450,6 +450,7 @@
     public void disconnect() {
         if ((mConnection != null) && (mSrcContext != null)) {
             mSrcContext.unbindService(mConnection);
+            mConnection = null;
         }
         try {
             // Send the DISCONNECTED, although it may not be received
@@ -463,10 +464,12 @@
         // Tell source we're disconnected.
         if (mSrcHandler != null) {
             replyDisconnected(STATUS_SUCCESSFUL);
+            mSrcHandler = null;
         }
         // Unlink only when bindService isn't used
         if (mConnection == null && mDstMessenger != null && mDeathMonitor!= null) {
             mDstMessenger.getBinder().unlinkToDeath(mDeathMonitor, 0);
+            mDeathMonitor = null;
         }
     }
 
diff --git a/core/java/com/android/internal/util/VirtualRefBasePtr.java b/core/java/com/android/internal/util/VirtualRefBasePtr.java
new file mode 100644
index 0000000..0bd4d3a
--- /dev/null
+++ b/core/java/com/android/internal/util/VirtualRefBasePtr.java
@@ -0,0 +1,47 @@
+/*
+ * 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.util;
+
+/**
+ * Helper class that contains a strong reference to a VirtualRefBase native
+ * object. This will incStrong in the ctor, and decStrong in the finalizer
+ */
+public final class VirtualRefBasePtr {
+    private long mNativePtr;
+
+    public VirtualRefBasePtr(long ptr) {
+        mNativePtr = ptr;
+        nIncStrong(mNativePtr);
+    }
+
+    public long get() {
+        return mNativePtr;
+    }
+
+    @Override
+    protected void finalize() throws Throwable {
+        try {
+            nDecStrong(mNativePtr);
+            mNativePtr = 0;
+        } finally {
+            super.finalize();
+        }
+    }
+
+    private static native void nIncStrong(long ptr);
+    private static native void nDecStrong(long ptr);
+}
diff --git a/core/java/com/android/internal/view/animation/FallbackLUTInterpolator.java b/core/java/com/android/internal/view/animation/FallbackLUTInterpolator.java
new file mode 100644
index 0000000..aec2b7e
--- /dev/null
+++ b/core/java/com/android/internal/view/animation/FallbackLUTInterpolator.java
@@ -0,0 +1,66 @@
+/*
+ * 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.view.animation;
+
+import android.animation.TimeInterpolator;
+import android.util.TimeUtils;
+import android.view.Choreographer;
+
+/**
+ * Interpolator that builds a lookup table to use. This is a fallback for
+ * building a native interpolator from a TimeInterpolator that is not marked
+ * with {@link HasNativeInterpolator}
+ */
+@HasNativeInterpolator
+public class FallbackLUTInterpolator implements NativeInterpolatorFactory {
+
+    private final float mLut[];
+
+    /**
+     * Used to cache the float[] LUT for use across multiple native
+     * interpolator creation
+     */
+    public FallbackLUTInterpolator(TimeInterpolator interpolator, int duration) {
+        mLut = createLUT(interpolator, duration);
+    }
+
+    private static float[] createLUT(TimeInterpolator interpolator, int duration) {
+        long frameIntervalNanos = Choreographer.getInstance().getFrameIntervalNanos();
+        int animIntervalMs = (int) (frameIntervalNanos / TimeUtils.NANOS_PER_MS);
+        int numAnimFrames = (int) Math.ceil(duration / animIntervalMs);
+        float values[] = new float[numAnimFrames];
+        float lastFrame = numAnimFrames - 1;
+        for (int i = 0; i < numAnimFrames; i++) {
+            float inValue = i / lastFrame;
+            values[i] = interpolator.getInterpolation(inValue);
+        }
+        return values;
+    }
+
+    @Override
+    public long createNativeInterpolator() {
+        return NativeInterpolatorFactoryHelper.createLutInterpolator(mLut);
+    }
+
+    /**
+     * Used to create a one-shot float[] LUT & native interpolator
+     */
+    public static long createNativeInterpolator(TimeInterpolator interpolator, int duration) {
+        float[] lut = createLUT(interpolator, duration);
+        return NativeInterpolatorFactoryHelper.createLutInterpolator(lut);
+    }
+}
diff --git a/core/java/com/android/internal/view/animation/HasNativeInterpolator.java b/core/java/com/android/internal/view/animation/HasNativeInterpolator.java
new file mode 100644
index 0000000..48ea4da
--- /dev/null
+++ b/core/java/com/android/internal/view/animation/HasNativeInterpolator.java
@@ -0,0 +1,37 @@
+/*
+ * 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.view.animation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * This is a class annotation that signals that it is safe to create
+ * a native interpolator counterpart via {@link NativeInterpolatorFactory}
+ *
+ * The idea here is to prevent subclasses of interpolators from being treated as a
+ * NativeInterpolatorFactory, and instead have them fall back to the LUT & LERP
+ * method like a custom interpolator.
+ *
+ * @hide
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.TYPE})
+public @interface HasNativeInterpolator {
+}
diff --git a/core/java/com/android/internal/view/animation/NativeInterpolatorFactory.java b/core/java/com/android/internal/view/animation/NativeInterpolatorFactory.java
new file mode 100644
index 0000000..fcacd52
--- /dev/null
+++ b/core/java/com/android/internal/view/animation/NativeInterpolatorFactory.java
@@ -0,0 +1,21 @@
+/*
+ * 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.view.animation;
+
+public interface NativeInterpolatorFactory {
+    long createNativeInterpolator();
+}
diff --git a/core/java/com/android/internal/view/animation/NativeInterpolatorFactoryHelper.java b/core/java/com/android/internal/view/animation/NativeInterpolatorFactoryHelper.java
new file mode 100644
index 0000000..7cd75f3
--- /dev/null
+++ b/core/java/com/android/internal/view/animation/NativeInterpolatorFactoryHelper.java
@@ -0,0 +1,36 @@
+/*
+ * 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.view.animation;
+
+/**
+ * Static utility class for constructing native interpolators to keep the
+ * JNI simpler
+ */
+public final class NativeInterpolatorFactoryHelper {
+    private NativeInterpolatorFactoryHelper() {}
+
+    public static native long createAccelerateDecelerateInterpolator();
+    public static native long createAccelerateInterpolator(float factor);
+    public static native long createAnticipateInterpolator(float tension);
+    public static native long createAnticipateOvershootInterpolator(float tension);
+    public static native long createBounceInterpolator();
+    public static native long createCycleInterpolator(float cycles);
+    public static native long createDecelerateInterpolator(float factor);
+    public static native long createLinearInterpolator();
+    public static native long createOvershootInterpolator(float tension);
+    public static native long createLutInterpolator(float[] values);
+}
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index 51e2871..7dc639d 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -155,7 +155,9 @@
 	android_content_res_Configuration.cpp \
 	android_animation_PropertyValuesHolder.cpp \
 	com_android_internal_net_NetworkStatsFactory.cpp \
-	com_android_internal_os_Zygote.cpp
+	com_android_internal_os_Zygote.cpp \
+	com_android_internal_util_VirtualRefBasePtr.cpp \
+	com_android_internal_view_animation_NativeInterpolatorFactoryHelper.cpp
 
 LOCAL_C_INCLUDES += \
 	$(JNI_H_INCLUDE) \
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 66fbb8e..01d8814 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -131,6 +131,7 @@
 extern int register_android_view_SurfaceControl(JNIEnv* env);
 extern int register_android_view_SurfaceSession(JNIEnv* env);
 extern int register_android_view_TextureView(JNIEnv* env);
+extern int register_com_android_internal_view_animation_NativeInterpolatorFactoryHelper(JNIEnv *env);
 extern int register_android_database_CursorWindow(JNIEnv* env);
 extern int register_android_database_SQLiteConnection(JNIEnv* env);
 extern int register_android_database_SQLiteGlobal(JNIEnv* env);
@@ -181,6 +182,7 @@
 extern int register_com_android_internal_content_NativeLibraryHelper(JNIEnv *env);
 extern int register_com_android_internal_net_NetworkStatsFactory(JNIEnv *env);
 extern int register_com_android_internal_os_Zygote(JNIEnv *env);
+extern int register_com_android_internal_util_VirtualRefBasePtr(JNIEnv *env);
 
 static AndroidRuntime* gCurRuntime = NULL;
 
@@ -1205,6 +1207,7 @@
     REG_JNI(register_android_view_SurfaceControl),
     REG_JNI(register_android_view_SurfaceSession),
     REG_JNI(register_android_view_TextureView),
+    REG_JNI(register_com_android_internal_view_animation_NativeInterpolatorFactoryHelper),
     REG_JNI(register_com_google_android_gles_jni_EGLImpl),
     REG_JNI(register_com_google_android_gles_jni_GLImpl),
     REG_JNI(register_android_opengl_jni_EGL14),
@@ -1262,6 +1265,7 @@
     REG_JNI(register_android_os_MemoryFile),
     REG_JNI(register_com_android_internal_os_ZygoteInit),
     REG_JNI(register_com_android_internal_os_Zygote),
+    REG_JNI(register_com_android_internal_util_VirtualRefBasePtr),
     REG_JNI(register_android_hardware_Camera),
     REG_JNI(register_android_hardware_camera2_CameraMetadata),
     REG_JNI(register_android_hardware_SensorManager),
diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp
index e0fa9ba4..0328517 100644
--- a/core/jni/android/graphics/Bitmap.cpp
+++ b/core/jni/android/graphics/Bitmap.cpp
@@ -1,838 +1,838 @@
-#include "SkBitmap.h"

-#include "SkPixelRef.h"

-#include "SkImageEncoder.h"

-#include "SkColorPriv.h"

-#include "GraphicsJNI.h"

-#include "SkDither.h"

-#include "SkUnPreMultiply.h"

-#include "SkStream.h"

-

-#include <binder/Parcel.h>

-#include "android_os_Parcel.h"

-#include "android_util_Binder.h"

-#include "android_nio_utils.h"

-#include "CreateJavaOutputStreamAdaptor.h"

-

-#include <jni.h>

-

-#include <Caches.h>

-

-#if 0

-    #define TRACE_BITMAP(code)  code

-#else

-    #define TRACE_BITMAP(code)

-#endif

-

-///////////////////////////////////////////////////////////////////////////////

-// Conversions to/from SkColor, for get/setPixels, and the create method, which

-// is basically like setPixels

-

-typedef void (*FromColorProc)(void* dst, const SkColor src[], int width,

-                              int x, int y);

-

-static void FromColor_D32(void* dst, const SkColor src[], int width,

-                          int, int) {

-    SkPMColor* d = (SkPMColor*)dst;

-

-    for (int i = 0; i < width; i++) {

-        *d++ = SkPreMultiplyColor(*src++);

-    }

-}

-

-static void FromColor_D32_Raw(void* dst, const SkColor src[], int width,

-                          int, int) {

-    // SkColor's ordering may be different from SkPMColor

-    if (SK_COLOR_MATCHES_PMCOLOR_BYTE_ORDER) {

-        memcpy(dst, src, width * sizeof(SkColor));

-        return;

-    }

-

-    // order isn't same, repack each pixel manually

-    SkPMColor* d = (SkPMColor*)dst;

-    for (int i = 0; i < width; i++) {

-        SkColor c = *src++;

-        *d++ = SkPackARGB32NoCheck(SkColorGetA(c), SkColorGetR(c),

-                                   SkColorGetG(c), SkColorGetB(c));

-    }

-}

-

-static void FromColor_D565(void* dst, const SkColor src[], int width,

-                           int x, int y) {

-    uint16_t* d = (uint16_t*)dst;

-

-    DITHER_565_SCAN(y);

-    for (int stop = x + width; x < stop; x++) {

-        SkColor c = *src++;

-        *d++ = SkDitherRGBTo565(SkColorGetR(c), SkColorGetG(c), SkColorGetB(c),

-                                DITHER_VALUE(x));

-    }

-}

-

-static void FromColor_D4444(void* dst, const SkColor src[], int width,

-                            int x, int y) {

-    SkPMColor16* d = (SkPMColor16*)dst;

-

-    DITHER_4444_SCAN(y);

-    for (int stop = x + width; x < stop; x++) {

-        SkPMColor pmc = SkPreMultiplyColor(*src++);

-        *d++ = SkDitherARGB32To4444(pmc, DITHER_VALUE(x));

-//        *d++ = SkPixel32ToPixel4444(pmc);

-    }

-}

-

-static void FromColor_D4444_Raw(void* dst, const SkColor src[], int width,

-                            int x, int y) {

-    SkPMColor16* d = (SkPMColor16*)dst;

-

-    DITHER_4444_SCAN(y);

-    for (int stop = x + width; x < stop; x++) {

-        SkColor c = *src++;

-

-        // SkPMColor is used because the ordering is ARGB32, even though the target actually premultiplied

-        SkPMColor pmc = SkPackARGB32NoCheck(SkColorGetA(c), SkColorGetR(c),

-                                            SkColorGetG(c), SkColorGetB(c));

-        *d++ = SkDitherARGB32To4444(pmc, DITHER_VALUE(x));

-//        *d++ = SkPixel32ToPixel4444(pmc);

-    }

-}

-

-// can return NULL

-static FromColorProc ChooseFromColorProc(SkBitmap::Config config, bool isPremultiplied) {

-    switch (config) {

-        case SkBitmap::kARGB_8888_Config:

-            return isPremultiplied ? FromColor_D32 : FromColor_D32_Raw;

-        case SkBitmap::kARGB_4444_Config:

-            return isPremultiplied ? FromColor_D4444 : FromColor_D4444_Raw;

-        case SkBitmap::kRGB_565_Config:

-            return FromColor_D565;

-        default:

-            break;

-    }

-    return NULL;

-}

-

-bool GraphicsJNI::SetPixels(JNIEnv* env, jintArray srcColors, int srcOffset, int srcStride,

-        int x, int y, int width, int height,

-        const SkBitmap& dstBitmap, bool isPremultiplied) {

-    SkAutoLockPixels alp(dstBitmap);

-    void* dst = dstBitmap.getPixels();

-    FromColorProc proc = ChooseFromColorProc(dstBitmap.config(), isPremultiplied);

-

-    if (NULL == dst || NULL == proc) {

-        return false;

-    }

-

-    const jint* array = env->GetIntArrayElements(srcColors, NULL);

-    const SkColor* src = (const SkColor*)array + srcOffset;

-

-    // reset to to actual choice from caller

-    dst = dstBitmap.getAddr(x, y);

-    // now copy/convert each scanline

-    for (int y = 0; y < height; y++) {

-        proc(dst, src, width, x, y);

-        src += srcStride;

-        dst = (char*)dst + dstBitmap.rowBytes();

-    }

-

-    dstBitmap.notifyPixelsChanged();

-

-    env->ReleaseIntArrayElements(srcColors, const_cast<jint*>(array),

-                                 JNI_ABORT);

-    return true;

-}

-

-//////////////////// ToColor procs

-

-typedef void (*ToColorProc)(SkColor dst[], const void* src, int width,

-                            SkColorTable*);

-

-static void ToColor_S32_Alpha(SkColor dst[], const void* src, int width,

-                              SkColorTable*) {

-    SkASSERT(width > 0);

-    const SkPMColor* s = (const SkPMColor*)src;

-    do {

-        *dst++ = SkUnPreMultiply::PMColorToColor(*s++);

-    } while (--width != 0);

-}

-

-static void ToColor_S32_Raw(SkColor dst[], const void* src, int width,

-                              SkColorTable*) {

-    SkASSERT(width > 0);

-    const SkPMColor* s = (const SkPMColor*)src;

-    do {

-        SkPMColor c = *s++;

-        *dst++ = SkColorSetARGB(SkGetPackedA32(c), SkGetPackedR32(c),

-                                SkGetPackedG32(c), SkGetPackedB32(c));

-    } while (--width != 0);

-}

-

-static void ToColor_S32_Opaque(SkColor dst[], const void* src, int width,

-                               SkColorTable*) {

-    SkASSERT(width > 0);

-    const SkPMColor* s = (const SkPMColor*)src;

-    do {

-        SkPMColor c = *s++;

-        *dst++ = SkColorSetRGB(SkGetPackedR32(c), SkGetPackedG32(c),

-                               SkGetPackedB32(c));

-    } while (--width != 0);

-}

-

-static void ToColor_S4444_Alpha(SkColor dst[], const void* src, int width,

-                                SkColorTable*) {

-    SkASSERT(width > 0);

-    const SkPMColor16* s = (const SkPMColor16*)src;

-    do {

-        *dst++ = SkUnPreMultiply::PMColorToColor(SkPixel4444ToPixel32(*s++));

-    } while (--width != 0);

-}

-

-static void ToColor_S4444_Raw(SkColor dst[], const void* src, int width,

-                                SkColorTable*) {

-    SkASSERT(width > 0);

-    const SkPMColor16* s = (const SkPMColor16*)src;

-    do {

-        SkPMColor c = SkPixel4444ToPixel32(*s++);

-        *dst++ = SkColorSetARGB(SkGetPackedA32(c), SkGetPackedR32(c),

-                                SkGetPackedG32(c), SkGetPackedB32(c));

-    } while (--width != 0);

-}

-

-static void ToColor_S4444_Opaque(SkColor dst[], const void* src, int width,

-                                 SkColorTable*) {

-    SkASSERT(width > 0);

-    const SkPMColor16* s = (const SkPMColor16*)src;

-    do {

-        SkPMColor c = SkPixel4444ToPixel32(*s++);

-        *dst++ = SkColorSetRGB(SkGetPackedR32(c), SkGetPackedG32(c),

-                               SkGetPackedB32(c));

-    } while (--width != 0);

-}

-

-static void ToColor_S565(SkColor dst[], const void* src, int width,

-                         SkColorTable*) {

-    SkASSERT(width > 0);

-    const uint16_t* s = (const uint16_t*)src;

-    do {

-        uint16_t c = *s++;

-        *dst++ =  SkColorSetRGB(SkPacked16ToR32(c), SkPacked16ToG32(c),

-                                SkPacked16ToB32(c));

-    } while (--width != 0);

-}

-

-static void ToColor_SI8_Alpha(SkColor dst[], const void* src, int width,

-                              SkColorTable* ctable) {

-    SkASSERT(width > 0);

-    const uint8_t* s = (const uint8_t*)src;

-    const SkPMColor* colors = ctable->lockColors();

-    do {

-        *dst++ = SkUnPreMultiply::PMColorToColor(colors[*s++]);

-    } while (--width != 0);

-    ctable->unlockColors();

-}

-

-static void ToColor_SI8_Raw(SkColor dst[], const void* src, int width,

-                              SkColorTable* ctable) {

-    SkASSERT(width > 0);

-    const uint8_t* s = (const uint8_t*)src;

-    const SkPMColor* colors = ctable->lockColors();

-    do {

-        SkPMColor c = colors[*s++];

-        *dst++ = SkColorSetARGB(SkGetPackedA32(c), SkGetPackedR32(c),

-                                SkGetPackedG32(c), SkGetPackedB32(c));

-    } while (--width != 0);

-    ctable->unlockColors();

-}

-

-static void ToColor_SI8_Opaque(SkColor dst[], const void* src, int width,

-                               SkColorTable* ctable) {

-    SkASSERT(width > 0);

-    const uint8_t* s = (const uint8_t*)src;

-    const SkPMColor* colors = ctable->lockColors();

-    do {

-        SkPMColor c = colors[*s++];

-        *dst++ = SkColorSetRGB(SkGetPackedR32(c), SkGetPackedG32(c),

-                               SkGetPackedB32(c));

-    } while (--width != 0);

-    ctable->unlockColors();

-}

-

-// can return NULL

-static ToColorProc ChooseToColorProc(const SkBitmap& src, bool isPremultiplied) {

-    switch (src.config()) {

-        case SkBitmap::kARGB_8888_Config:

-            if (src.isOpaque()) return ToColor_S32_Opaque;

-            return isPremultiplied ? ToColor_S32_Alpha : ToColor_S32_Raw;

-        case SkBitmap::kARGB_4444_Config:

-            if (src.isOpaque()) return ToColor_S4444_Opaque;

-            return isPremultiplied ? ToColor_S4444_Alpha : ToColor_S4444_Raw;

-        case SkBitmap::kRGB_565_Config:

-            return ToColor_S565;

-        case SkBitmap::kIndex8_Config:

-            if (src.getColorTable() == NULL) {

-                return NULL;

-            }

-            if (src.isOpaque()) return ToColor_SI8_Opaque;

-            return isPremultiplied ? ToColor_SI8_Raw : ToColor_SI8_Alpha;

-        default:

-            break;

-    }

-    return NULL;

-}

-

-///////////////////////////////////////////////////////////////////////////////

-///////////////////////////////////////////////////////////////////////////////

-

-static int getPremulBitmapCreateFlags(bool isMutable) {

-    int flags = GraphicsJNI::kBitmapCreateFlag_Premultiplied;

-    if (isMutable) flags |= GraphicsJNI::kBitmapCreateFlag_Mutable;

-    return flags;

-}

-

-static jobject Bitmap_creator(JNIEnv* env, jobject, jintArray jColors,

-                              jint offset, jint stride, jint width, jint height,

-                              jint configHandle, jboolean isMutable) {

-    SkBitmap::Config config = static_cast<SkBitmap::Config>(configHandle);

-    if (NULL != jColors) {

-        size_t n = env->GetArrayLength(jColors);

-        if (n < SkAbs32(stride) * (size_t)height) {

-            doThrowAIOOBE(env);

-            return NULL;

-        }

-    }

-

-    // ARGB_4444 is a deprecated format, convert automatically to 8888

-    if (config == SkBitmap::kARGB_4444_Config) {

-        config = SkBitmap::kARGB_8888_Config;

-    }

-

-    SkBitmap bitmap;

-    bitmap.setConfig(config, width, height);

-

-    jbyteArray buff = GraphicsJNI::allocateJavaPixelRef(env, &bitmap, NULL);

-    if (NULL == buff) {

-        return NULL;

-    }

-

-    if (jColors != NULL) {

-        GraphicsJNI::SetPixels(env, jColors, offset, stride,

-                0, 0, width, height, bitmap, true);

-    }

-

-    return GraphicsJNI::createBitmap(env, new SkBitmap(bitmap), buff,

-            getPremulBitmapCreateFlags(isMutable), NULL, NULL);

-}

-

-static jobject Bitmap_copy(JNIEnv* env, jobject, jlong srcHandle,

-                           jint dstConfigHandle, jboolean isMutable) {

-    const SkBitmap* src = reinterpret_cast<SkBitmap*>(srcHandle);

-    SkBitmap::Config dstConfig = static_cast<SkBitmap::Config>(dstConfigHandle);

-    SkBitmap            result;

-    JavaPixelAllocator  allocator(env);

-

+#include "SkBitmap.h"
+#include "SkPixelRef.h"
+#include "SkImageEncoder.h"
+#include "SkColorPriv.h"
+#include "GraphicsJNI.h"
+#include "SkDither.h"
+#include "SkUnPreMultiply.h"
+#include "SkStream.h"
+
+#include <binder/Parcel.h>
+#include "android_os_Parcel.h"
+#include "android_util_Binder.h"
+#include "android_nio_utils.h"
+#include "CreateJavaOutputStreamAdaptor.h"
+
+#include <jni.h>
+
+#include <Caches.h>
+
+#if 0
+    #define TRACE_BITMAP(code)  code
+#else
+    #define TRACE_BITMAP(code)
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+// Conversions to/from SkColor, for get/setPixels, and the create method, which
+// is basically like setPixels
+
+typedef void (*FromColorProc)(void* dst, const SkColor src[], int width,
+                              int x, int y);
+
+static void FromColor_D32(void* dst, const SkColor src[], int width,
+                          int, int) {
+    SkPMColor* d = (SkPMColor*)dst;
+
+    for (int i = 0; i < width; i++) {
+        *d++ = SkPreMultiplyColor(*src++);
+    }
+}
+
+static void FromColor_D32_Raw(void* dst, const SkColor src[], int width,
+                          int, int) {
+    // SkColor's ordering may be different from SkPMColor
+    if (SK_COLOR_MATCHES_PMCOLOR_BYTE_ORDER) {
+        memcpy(dst, src, width * sizeof(SkColor));
+        return;
+    }
+
+    // order isn't same, repack each pixel manually
+    SkPMColor* d = (SkPMColor*)dst;
+    for (int i = 0; i < width; i++) {
+        SkColor c = *src++;
+        *d++ = SkPackARGB32NoCheck(SkColorGetA(c), SkColorGetR(c),
+                                   SkColorGetG(c), SkColorGetB(c));
+    }
+}
+
+static void FromColor_D565(void* dst, const SkColor src[], int width,
+                           int x, int y) {
+    uint16_t* d = (uint16_t*)dst;
+
+    DITHER_565_SCAN(y);
+    for (int stop = x + width; x < stop; x++) {
+        SkColor c = *src++;
+        *d++ = SkDitherRGBTo565(SkColorGetR(c), SkColorGetG(c), SkColorGetB(c),
+                                DITHER_VALUE(x));
+    }
+}
+
+static void FromColor_D4444(void* dst, const SkColor src[], int width,
+                            int x, int y) {
+    SkPMColor16* d = (SkPMColor16*)dst;
+
+    DITHER_4444_SCAN(y);
+    for (int stop = x + width; x < stop; x++) {
+        SkPMColor pmc = SkPreMultiplyColor(*src++);
+        *d++ = SkDitherARGB32To4444(pmc, DITHER_VALUE(x));
+//        *d++ = SkPixel32ToPixel4444(pmc);
+    }
+}
+
+static void FromColor_D4444_Raw(void* dst, const SkColor src[], int width,
+                            int x, int y) {
+    SkPMColor16* d = (SkPMColor16*)dst;
+
+    DITHER_4444_SCAN(y);
+    for (int stop = x + width; x < stop; x++) {
+        SkColor c = *src++;
+
+        // SkPMColor is used because the ordering is ARGB32, even though the target actually premultiplied
+        SkPMColor pmc = SkPackARGB32NoCheck(SkColorGetA(c), SkColorGetR(c),
+                                            SkColorGetG(c), SkColorGetB(c));
+        *d++ = SkDitherARGB32To4444(pmc, DITHER_VALUE(x));
+//        *d++ = SkPixel32ToPixel4444(pmc);
+    }
+}
+
+// can return NULL
+static FromColorProc ChooseFromColorProc(SkBitmap::Config config, bool isPremultiplied) {
+    switch (config) {
+        case SkBitmap::kARGB_8888_Config:
+            return isPremultiplied ? FromColor_D32 : FromColor_D32_Raw;
+        case SkBitmap::kARGB_4444_Config:
+            return isPremultiplied ? FromColor_D4444 : FromColor_D4444_Raw;
+        case SkBitmap::kRGB_565_Config:
+            return FromColor_D565;
+        default:
+            break;
+    }
+    return NULL;
+}
+
+bool GraphicsJNI::SetPixels(JNIEnv* env, jintArray srcColors, int srcOffset, int srcStride,
+        int x, int y, int width, int height,
+        const SkBitmap& dstBitmap, bool isPremultiplied) {
+    SkAutoLockPixels alp(dstBitmap);
+    void* dst = dstBitmap.getPixels();
+    FromColorProc proc = ChooseFromColorProc(dstBitmap.config(), isPremultiplied);
+
+    if (NULL == dst || NULL == proc) {
+        return false;
+    }
+
+    const jint* array = env->GetIntArrayElements(srcColors, NULL);
+    const SkColor* src = (const SkColor*)array + srcOffset;
+
+    // reset to to actual choice from caller
+    dst = dstBitmap.getAddr(x, y);
+    // now copy/convert each scanline
+    for (int y = 0; y < height; y++) {
+        proc(dst, src, width, x, y);
+        src += srcStride;
+        dst = (char*)dst + dstBitmap.rowBytes();
+    }
+
+    dstBitmap.notifyPixelsChanged();
+
+    env->ReleaseIntArrayElements(srcColors, const_cast<jint*>(array),
+                                 JNI_ABORT);
+    return true;
+}
+
+//////////////////// ToColor procs
+
+typedef void (*ToColorProc)(SkColor dst[], const void* src, int width,
+                            SkColorTable*);
+
+static void ToColor_S32_Alpha(SkColor dst[], const void* src, int width,
+                              SkColorTable*) {
+    SkASSERT(width > 0);
+    const SkPMColor* s = (const SkPMColor*)src;
+    do {
+        *dst++ = SkUnPreMultiply::PMColorToColor(*s++);
+    } while (--width != 0);
+}
+
+static void ToColor_S32_Raw(SkColor dst[], const void* src, int width,
+                              SkColorTable*) {
+    SkASSERT(width > 0);
+    const SkPMColor* s = (const SkPMColor*)src;
+    do {
+        SkPMColor c = *s++;
+        *dst++ = SkColorSetARGB(SkGetPackedA32(c), SkGetPackedR32(c),
+                                SkGetPackedG32(c), SkGetPackedB32(c));
+    } while (--width != 0);
+}
+
+static void ToColor_S32_Opaque(SkColor dst[], const void* src, int width,
+                               SkColorTable*) {
+    SkASSERT(width > 0);
+    const SkPMColor* s = (const SkPMColor*)src;
+    do {
+        SkPMColor c = *s++;
+        *dst++ = SkColorSetRGB(SkGetPackedR32(c), SkGetPackedG32(c),
+                               SkGetPackedB32(c));
+    } while (--width != 0);
+}
+
+static void ToColor_S4444_Alpha(SkColor dst[], const void* src, int width,
+                                SkColorTable*) {
+    SkASSERT(width > 0);
+    const SkPMColor16* s = (const SkPMColor16*)src;
+    do {
+        *dst++ = SkUnPreMultiply::PMColorToColor(SkPixel4444ToPixel32(*s++));
+    } while (--width != 0);
+}
+
+static void ToColor_S4444_Raw(SkColor dst[], const void* src, int width,
+                                SkColorTable*) {
+    SkASSERT(width > 0);
+    const SkPMColor16* s = (const SkPMColor16*)src;
+    do {
+        SkPMColor c = SkPixel4444ToPixel32(*s++);
+        *dst++ = SkColorSetARGB(SkGetPackedA32(c), SkGetPackedR32(c),
+                                SkGetPackedG32(c), SkGetPackedB32(c));
+    } while (--width != 0);
+}
+
+static void ToColor_S4444_Opaque(SkColor dst[], const void* src, int width,
+                                 SkColorTable*) {
+    SkASSERT(width > 0);
+    const SkPMColor16* s = (const SkPMColor16*)src;
+    do {
+        SkPMColor c = SkPixel4444ToPixel32(*s++);
+        *dst++ = SkColorSetRGB(SkGetPackedR32(c), SkGetPackedG32(c),
+                               SkGetPackedB32(c));
+    } while (--width != 0);
+}
+
+static void ToColor_S565(SkColor dst[], const void* src, int width,
+                         SkColorTable*) {
+    SkASSERT(width > 0);
+    const uint16_t* s = (const uint16_t*)src;
+    do {
+        uint16_t c = *s++;
+        *dst++ =  SkColorSetRGB(SkPacked16ToR32(c), SkPacked16ToG32(c),
+                                SkPacked16ToB32(c));
+    } while (--width != 0);
+}
+
+static void ToColor_SI8_Alpha(SkColor dst[], const void* src, int width,
+                              SkColorTable* ctable) {
+    SkASSERT(width > 0);
+    const uint8_t* s = (const uint8_t*)src;
+    const SkPMColor* colors = ctable->lockColors();
+    do {
+        *dst++ = SkUnPreMultiply::PMColorToColor(colors[*s++]);
+    } while (--width != 0);
+    ctable->unlockColors();
+}
+
+static void ToColor_SI8_Raw(SkColor dst[], const void* src, int width,
+                              SkColorTable* ctable) {
+    SkASSERT(width > 0);
+    const uint8_t* s = (const uint8_t*)src;
+    const SkPMColor* colors = ctable->lockColors();
+    do {
+        SkPMColor c = colors[*s++];
+        *dst++ = SkColorSetARGB(SkGetPackedA32(c), SkGetPackedR32(c),
+                                SkGetPackedG32(c), SkGetPackedB32(c));
+    } while (--width != 0);
+    ctable->unlockColors();
+}
+
+static void ToColor_SI8_Opaque(SkColor dst[], const void* src, int width,
+                               SkColorTable* ctable) {
+    SkASSERT(width > 0);
+    const uint8_t* s = (const uint8_t*)src;
+    const SkPMColor* colors = ctable->lockColors();
+    do {
+        SkPMColor c = colors[*s++];
+        *dst++ = SkColorSetRGB(SkGetPackedR32(c), SkGetPackedG32(c),
+                               SkGetPackedB32(c));
+    } while (--width != 0);
+    ctable->unlockColors();
+}
+
+// can return NULL
+static ToColorProc ChooseToColorProc(const SkBitmap& src, bool isPremultiplied) {
+    switch (src.config()) {
+        case SkBitmap::kARGB_8888_Config:
+            if (src.isOpaque()) return ToColor_S32_Opaque;
+            return isPremultiplied ? ToColor_S32_Alpha : ToColor_S32_Raw;
+        case SkBitmap::kARGB_4444_Config:
+            if (src.isOpaque()) return ToColor_S4444_Opaque;
+            return isPremultiplied ? ToColor_S4444_Alpha : ToColor_S4444_Raw;
+        case SkBitmap::kRGB_565_Config:
+            return ToColor_S565;
+        case SkBitmap::kIndex8_Config:
+            if (src.getColorTable() == NULL) {
+                return NULL;
+            }
+            if (src.isOpaque()) return ToColor_SI8_Opaque;
+            return isPremultiplied ? ToColor_SI8_Raw : ToColor_SI8_Alpha;
+        default:
+            break;
+    }
+    return NULL;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+static int getPremulBitmapCreateFlags(bool isMutable) {
+    int flags = GraphicsJNI::kBitmapCreateFlag_Premultiplied;
+    if (isMutable) flags |= GraphicsJNI::kBitmapCreateFlag_Mutable;
+    return flags;
+}
+
+static jobject Bitmap_creator(JNIEnv* env, jobject, jintArray jColors,
+                              jint offset, jint stride, jint width, jint height,
+                              jint configHandle, jboolean isMutable) {
+    SkBitmap::Config config = static_cast<SkBitmap::Config>(configHandle);
+    if (NULL != jColors) {
+        size_t n = env->GetArrayLength(jColors);
+        if (n < SkAbs32(stride) * (size_t)height) {
+            doThrowAIOOBE(env);
+            return NULL;
+        }
+    }
+
+    // ARGB_4444 is a deprecated format, convert automatically to 8888
+    if (config == SkBitmap::kARGB_4444_Config) {
+        config = SkBitmap::kARGB_8888_Config;
+    }
+
+    SkBitmap bitmap;
+    bitmap.setConfig(config, width, height);
+
+    jbyteArray buff = GraphicsJNI::allocateJavaPixelRef(env, &bitmap, NULL);
+    if (NULL == buff) {
+        return NULL;
+    }
+
+    if (jColors != NULL) {
+        GraphicsJNI::SetPixels(env, jColors, offset, stride,
+                0, 0, width, height, bitmap, true);
+    }
+
+    return GraphicsJNI::createBitmap(env, new SkBitmap(bitmap), buff,
+            getPremulBitmapCreateFlags(isMutable), NULL, NULL);
+}
+
+static jobject Bitmap_copy(JNIEnv* env, jobject, jlong srcHandle,
+                           jint dstConfigHandle, jboolean isMutable) {
+    const SkBitmap* src = reinterpret_cast<SkBitmap*>(srcHandle);
+    SkBitmap::Config dstConfig = static_cast<SkBitmap::Config>(dstConfigHandle);
+    SkBitmap            result;
+    JavaPixelAllocator  allocator(env);
+
     if (!src->copyTo(&result, SkBitmapConfigToColorType(dstConfig), &allocator)) {
-        return NULL;

-    }

-    return GraphicsJNI::createBitmap(env, new SkBitmap(result), allocator.getStorageObj(),

-            getPremulBitmapCreateFlags(isMutable), NULL, NULL);

-}

-

-static void Bitmap_destructor(JNIEnv* env, jobject, jlong bitmapHandle) {

-    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);

-#ifdef USE_OPENGL_RENDERER

-    if (android::uirenderer::Caches::hasInstance()) {

-        android::uirenderer::Caches::getInstance().resourceCache.destructor(bitmap);

-        return;

-    }

-#endif // USE_OPENGL_RENDERER

-    delete bitmap;

-}

-

-static jboolean Bitmap_recycle(JNIEnv* env, jobject, jlong bitmapHandle) {

-    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);

-#ifdef USE_OPENGL_RENDERER

-    if (android::uirenderer::Caches::hasInstance()) {

-        bool result;

-        result = android::uirenderer::Caches::getInstance().resourceCache.recycle(bitmap);

-        return result ? JNI_TRUE : JNI_FALSE;

-    }

-#endif // USE_OPENGL_RENDERER

-    bitmap->setPixels(NULL, NULL);

-    return JNI_TRUE;

-}

-

-static void Bitmap_reconfigure(JNIEnv* env, jobject clazz, jlong bitmapHandle,

-        jint width, jint height, jint configHandle, jint allocSize) {

-    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);

-    SkBitmap::Config config = static_cast<SkBitmap::Config>(configHandle);    

-    if (width * height * SkBitmap::ComputeBytesPerPixel(config) > allocSize) {

-        // done in native as there's no way to get BytesPerPixel in Java

-        doThrowIAE(env, "Bitmap not large enough to support new configuration");

-        return;

-    }

-    SkPixelRef* ref = bitmap->pixelRef();

-    SkSafeRef(ref);

-    bitmap->setConfig(config, width, height);

-    bitmap->setPixelRef(ref);

-

-    // notifyPixelsChanged will increment the generation ID even though the actual pixel data

-    // hasn't been touched. This signals the renderer that the bitmap (including width, height,

-    // and config) has changed.

-    ref->notifyPixelsChanged();

-    SkSafeUnref(ref);

-}

-

-// These must match the int values in Bitmap.java

-enum JavaEncodeFormat {

-    kJPEG_JavaEncodeFormat = 0,

-    kPNG_JavaEncodeFormat = 1,

-    kWEBP_JavaEncodeFormat = 2

-};

-

-static jboolean Bitmap_compress(JNIEnv* env, jobject clazz, jlong bitmapHandle,

-                                jint format, jint quality,

-                                jobject jstream, jbyteArray jstorage) {

-    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);

-    SkImageEncoder::Type fm;

-

-    switch (format) {

-    case kJPEG_JavaEncodeFormat:

-        fm = SkImageEncoder::kJPEG_Type;

-        break;

-    case kPNG_JavaEncodeFormat:

-        fm = SkImageEncoder::kPNG_Type;

-        break;

-    case kWEBP_JavaEncodeFormat:

-        fm = SkImageEncoder::kWEBP_Type;

-        break;

-    default:

-        return JNI_FALSE;

-    }

-

-    bool success = false;

-    if (NULL != bitmap) {

-        SkAutoLockPixels alp(*bitmap);

-

-        if (NULL == bitmap->getPixels()) {

-            return JNI_FALSE;

-        }

-

-        SkWStream* strm = CreateJavaOutputStreamAdaptor(env, jstream, jstorage);

-        if (NULL == strm) {

-            return JNI_FALSE;

-        }

-

-        SkImageEncoder* encoder = SkImageEncoder::Create(fm);

-        if (NULL != encoder) {

-            success = encoder->encodeStream(strm, *bitmap, quality);

-            delete encoder;

-        }

-        delete strm;

-    }

-    return success ? JNI_TRUE : JNI_FALSE;

-}

-

-static void Bitmap_erase(JNIEnv* env, jobject, jlong bitmapHandle, jint color) {

-    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);

-    bitmap->eraseColor(color);

-}

-

-static jint Bitmap_rowBytes(JNIEnv* env, jobject, jlong bitmapHandle) {

-    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);

-    return static_cast<jint>(bitmap->rowBytes());

-}

-

-static jint Bitmap_config(JNIEnv* env, jobject, jlong bitmapHandle) {

-    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);

-    return static_cast<jint>(bitmap->config());

-}

-

-static jint Bitmap_getGenerationId(JNIEnv* env, jobject, jlong bitmapHandle) {

-    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);

-    return static_cast<jint>(bitmap->getGenerationID());

-}

-

-static jboolean Bitmap_hasAlpha(JNIEnv* env, jobject, jlong bitmapHandle) {

-    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);

-    return !bitmap->isOpaque() ? JNI_TRUE : JNI_FALSE;

-}

-

-static void Bitmap_setAlphaAndPremultiplied(JNIEnv* env, jobject, jlong bitmapHandle,

-                                            jboolean hasAlpha, jboolean isPremul) {

-    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);

-    if (!hasAlpha) {

-        bitmap->setAlphaType(kOpaque_SkAlphaType);

-    } else if (isPremul) {

-        bitmap->setAlphaType(kPremul_SkAlphaType);

-    } else {

-        bitmap->setAlphaType(kUnpremul_SkAlphaType);

-    }

-}

-

-static jboolean Bitmap_hasMipMap(JNIEnv* env, jobject, jlong bitmapHandle) {

-    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);

-    return bitmap->hasHardwareMipMap() ? JNI_TRUE : JNI_FALSE;

-}

-

-static void Bitmap_setHasMipMap(JNIEnv* env, jobject, jlong bitmapHandle,

-                                jboolean hasMipMap) {

-    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);

-    bitmap->setHasHardwareMipMap(hasMipMap);

-}

-

-///////////////////////////////////////////////////////////////////////////////

-

-static jobject Bitmap_createFromParcel(JNIEnv* env, jobject, jobject parcel) {

-    if (parcel == NULL) {

-        SkDebugf("-------- unparcel parcel is NULL\n");

-        return NULL;

-    }

-

-    android::Parcel* p = android::parcelForJavaObject(env, parcel);

-

-    const bool              isMutable = p->readInt32() != 0;

-    const SkBitmap::Config  config = (SkBitmap::Config)p->readInt32();

-    const int               width = p->readInt32();

-    const int               height = p->readInt32();

-    const int               rowBytes = p->readInt32();

-    const int               density = p->readInt32();

-

-    if (SkBitmap::kARGB_8888_Config != config &&

-            SkBitmap::kRGB_565_Config != config &&

-            SkBitmap::kARGB_4444_Config != config &&

-            SkBitmap::kIndex8_Config != config &&

-            SkBitmap::kA8_Config != config) {

-        SkDebugf("Bitmap_createFromParcel unknown config: %d\n", config);

-        return NULL;

-    }

-

-    SkBitmap* bitmap = new SkBitmap;

-

-    bitmap->setConfig(config, width, height, rowBytes);

-

-    SkColorTable* ctable = NULL;

-    if (config == SkBitmap::kIndex8_Config) {

-        int count = p->readInt32();

-        if (count > 0) {

-            size_t size = count * sizeof(SkPMColor);

-            const SkPMColor* src = (const SkPMColor*)p->readInplace(size);

-            ctable = new SkColorTable(src, count);

-        }

-    }

-

-    jbyteArray buffer = GraphicsJNI::allocateJavaPixelRef(env, bitmap, ctable);

-    if (NULL == buffer) {

-        SkSafeUnref(ctable);

-        delete bitmap;

-        return NULL;

-    }

-

-    SkSafeUnref(ctable);

-

-    size_t size = bitmap->getSize();

-

-    android::Parcel::ReadableBlob blob;

-    android::status_t status = p->readBlob(size, &blob);

-    if (status) {

-        doThrowRE(env, "Could not read bitmap from parcel blob.");

-        delete bitmap;

-        return NULL;

-    }

-

-    bitmap->lockPixels();

-    memcpy(bitmap->getPixels(), blob.data(), size);

-    bitmap->unlockPixels();

-

-    blob.release();

-

-    return GraphicsJNI::createBitmap(env, bitmap, buffer, getPremulBitmapCreateFlags(isMutable),

-            NULL, NULL, density);

-}

-

-static jboolean Bitmap_writeToParcel(JNIEnv* env, jobject,

-                                     jlong bitmapHandle,

-                                     jboolean isMutable, jint density,

-                                     jobject parcel) {

-    const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);

-    if (parcel == NULL) {

-        SkDebugf("------- writeToParcel null parcel\n");

-        return JNI_FALSE;

-    }

-

-    android::Parcel* p = android::parcelForJavaObject(env, parcel);

-

-    p->writeInt32(isMutable);

-    p->writeInt32(bitmap->config());

-    p->writeInt32(bitmap->width());

-    p->writeInt32(bitmap->height());

-    p->writeInt32(bitmap->rowBytes());

-    p->writeInt32(density);

-

-    if (bitmap->config() == SkBitmap::kIndex8_Config) {

-        SkColorTable* ctable = bitmap->getColorTable();

-        if (ctable != NULL) {

-            int count = ctable->count();

-            p->writeInt32(count);

-            memcpy(p->writeInplace(count * sizeof(SkPMColor)),

-                   ctable->lockColors(), count * sizeof(SkPMColor));

-            ctable->unlockColors();

-        } else {

-            p->writeInt32(0);   // indicate no ctable

-        }

-    }

-

-    size_t size = bitmap->getSize();

-

-    android::Parcel::WritableBlob blob;

-    android::status_t status = p->writeBlob(size, &blob);

-    if (status) {

-        doThrowRE(env, "Could not write bitmap to parcel blob.");

-        return JNI_FALSE;

-    }

-

-    bitmap->lockPixels();

-    const void* pSrc =  bitmap->getPixels();

-    if (pSrc == NULL) {

-        memset(blob.data(), 0, size);

-    } else {

-        memcpy(blob.data(), pSrc, size);

-    }

-    bitmap->unlockPixels();

-

-    blob.release();

-    return JNI_TRUE;

-}

-

-static jobject Bitmap_extractAlpha(JNIEnv* env, jobject clazz,

-                                   jlong srcHandle, jlong paintHandle,

-                                   jintArray offsetXY) {

-    const SkBitmap* src = reinterpret_cast<SkBitmap*>(srcHandle);

-    const SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);

-    SkIPoint  offset;

-    SkBitmap* dst = new SkBitmap;

-    JavaPixelAllocator allocator(env);

-

-    src->extractAlpha(dst, paint, &allocator, &offset);

-    // If Skia can't allocate pixels for destination bitmap, it resets

-    // it, that is set its pixels buffer to NULL, and zero width and height.

-    if (dst->getPixels() == NULL && src->getPixels() != NULL) {

-        delete dst;

-        doThrowOOME(env, "failed to allocate pixels for alpha");

-        return NULL;

-    }

-    if (offsetXY != 0 && env->GetArrayLength(offsetXY) >= 2) {

-        int* array = env->GetIntArrayElements(offsetXY, NULL);

-        array[0] = offset.fX;

-        array[1] = offset.fY;

-        env->ReleaseIntArrayElements(offsetXY, array, 0);

-    }

-

-    return GraphicsJNI::createBitmap(env, dst, allocator.getStorageObj(),

-            getPremulBitmapCreateFlags(true), NULL, NULL);

-}

-

-///////////////////////////////////////////////////////////////////////////////

-

-static jint Bitmap_getPixel(JNIEnv* env, jobject, jlong bitmapHandle,

-        jint x, jint y, jboolean isPremultiplied) {

-    const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);

-    SkAutoLockPixels alp(*bitmap);

-

-    ToColorProc proc = ChooseToColorProc(*bitmap, isPremultiplied);

-    if (NULL == proc) {

-        return 0;

-    }

-    const void* src = bitmap->getAddr(x, y);

-    if (NULL == src) {

-        return 0;

-    }

-

-    SkColor dst[1];

-    proc(dst, src, 1, bitmap->getColorTable());

-    return static_cast<jint>(dst[0]);

-}

-

-static void Bitmap_getPixels(JNIEnv* env, jobject, jlong bitmapHandle,

-        jintArray pixelArray, jint offset, jint stride,

-        jint x, jint y, jint width, jint height, jboolean isPremultiplied) {

-    const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);

-    SkAutoLockPixels alp(*bitmap);

-

-    ToColorProc proc = ChooseToColorProc(*bitmap, isPremultiplied);

-    if (NULL == proc) {

-        return;

-    }

-    const void* src = bitmap->getAddr(x, y);

-    if (NULL == src) {

-        return;

-    }

-

-    SkColorTable* ctable = bitmap->getColorTable();

-    jint* dst = env->GetIntArrayElements(pixelArray, NULL);

-    SkColor* d = (SkColor*)dst + offset;

-    while (--height >= 0) {

-        proc(d, src, width, ctable);

-        d += stride;

-        src = (void*)((const char*)src + bitmap->rowBytes());

-    }

-    env->ReleaseIntArrayElements(pixelArray, dst, 0);

-}

-

-///////////////////////////////////////////////////////////////////////////////

-

-static void Bitmap_setPixel(JNIEnv* env, jobject, jlong bitmapHandle,

-        jint x, jint y, jint colorHandle, jboolean isPremultiplied) {

-    const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);

-    SkColor color = static_cast<SkColor>(colorHandle);

-    SkAutoLockPixels alp(*bitmap);

-    if (NULL == bitmap->getPixels()) {

-        return;

-    }

-

-    FromColorProc proc = ChooseFromColorProc(bitmap->config(), isPremultiplied);

-    if (NULL == proc) {

-        return;

-    }

-

-    proc(bitmap->getAddr(x, y), &color, 1, x, y);

-    bitmap->notifyPixelsChanged();

-}

-

-static void Bitmap_setPixels(JNIEnv* env, jobject, jlong bitmapHandle,

-        jintArray pixelArray, jint offset, jint stride,

-        jint x, jint y, jint width, jint height, jboolean isPremultiplied) {

-    const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);

-    GraphicsJNI::SetPixels(env, pixelArray, offset, stride,

-            x, y, width, height, *bitmap, isPremultiplied);

-}

-

-static void Bitmap_copyPixelsToBuffer(JNIEnv* env, jobject,

-                                      jlong bitmapHandle, jobject jbuffer) {

-    const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);

-    SkAutoLockPixels alp(*bitmap);

-    const void* src = bitmap->getPixels();

-

-    if (NULL != src) {

-        android::AutoBufferPointer abp(env, jbuffer, JNI_TRUE);

-

-        // the java side has already checked that buffer is large enough

-        memcpy(abp.pointer(), src, bitmap->getSize());

-    }

-}

-

-static void Bitmap_copyPixelsFromBuffer(JNIEnv* env, jobject,

-                                        jlong bitmapHandle, jobject jbuffer) {

-    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);

-    SkAutoLockPixels alp(*bitmap);

-    void* dst = bitmap->getPixels();

-

-    if (NULL != dst) {

-        android::AutoBufferPointer abp(env, jbuffer, JNI_FALSE);

-        // the java side has already checked that buffer is large enough

-        memcpy(dst, abp.pointer(), bitmap->getSize());

-        bitmap->notifyPixelsChanged();

-    }

-}

-

-static jboolean Bitmap_sameAs(JNIEnv* env, jobject, jlong bm0Handle,

-                              jlong bm1Handle) {

-    const SkBitmap* bm0 = reinterpret_cast<SkBitmap*>(bm0Handle);

-    const SkBitmap* bm1 = reinterpret_cast<SkBitmap*>(bm1Handle);

-    if (bm0->width() != bm1->width() ||

-        bm0->height() != bm1->height() ||

-        bm0->config() != bm1->config()) {

-        return JNI_FALSE;

-    }

-

-    SkAutoLockPixels alp0(*bm0);

-    SkAutoLockPixels alp1(*bm1);

-

-    // if we can't load the pixels, return false

-    if (NULL == bm0->getPixels() || NULL == bm1->getPixels()) {

-        return JNI_FALSE;

-    }

-

-    if (bm0->config() == SkBitmap::kIndex8_Config) {

-        SkColorTable* ct0 = bm0->getColorTable();

-        SkColorTable* ct1 = bm1->getColorTable();

-        if (NULL == ct0 || NULL == ct1) {

-            return JNI_FALSE;

-        }

-        if (ct0->count() != ct1->count()) {

-            return JNI_FALSE;

-        }

-

-        SkAutoLockColors alc0(ct0);

-        SkAutoLockColors alc1(ct1);

-        const size_t size = ct0->count() * sizeof(SkPMColor);

-        if (memcmp(alc0.colors(), alc1.colors(), size) != 0) {

-            return JNI_FALSE;

-        }

-    }

-

-    // now compare each scanline. We can't do the entire buffer at once,

-    // since we don't care about the pixel values that might extend beyond

-    // the width (since the scanline might be larger than the logical width)

-    const int h = bm0->height();

-    const size_t size = bm0->width() * bm0->bytesPerPixel();

-    for (int y = 0; y < h; y++) {

-        if (memcmp(bm0->getAddr(0, y), bm1->getAddr(0, y), size) != 0) {

-            return JNI_FALSE;

-        }

-    }

-    return JNI_TRUE;

-}

-

-static void Bitmap_prepareToDraw(JNIEnv* env, jobject, jlong bitmapHandle) {

-    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);

-    bitmap->lockPixels();

-    bitmap->unlockPixels();

-}

-

-///////////////////////////////////////////////////////////////////////////////

-

-#include <android_runtime/AndroidRuntime.h>

-

-static JNINativeMethod gBitmapMethods[] = {

-    {   "nativeCreate",             "([IIIIIIZ)Landroid/graphics/Bitmap;",

-        (void*)Bitmap_creator },

-    {   "nativeCopy",               "(JIZ)Landroid/graphics/Bitmap;",

-        (void*)Bitmap_copy },

-    {   "nativeDestructor",         "(J)V", (void*)Bitmap_destructor },

-    {   "nativeRecycle",            "(J)Z", (void*)Bitmap_recycle },

-    {   "nativeReconfigure",        "(JIIII)V", (void*)Bitmap_reconfigure },

-    {   "nativeCompress",           "(JIILjava/io/OutputStream;[B)Z",

-        (void*)Bitmap_compress },

-    {   "nativeErase",              "(JI)V", (void*)Bitmap_erase },

-    {   "nativeRowBytes",           "(J)I", (void*)Bitmap_rowBytes },

-    {   "nativeConfig",             "(J)I", (void*)Bitmap_config },

-    {   "nativeHasAlpha",           "(J)Z", (void*)Bitmap_hasAlpha },

-    {   "nativeSetAlphaAndPremultiplied", "(JZZ)V", (void*)Bitmap_setAlphaAndPremultiplied},

-    {   "nativeHasMipMap",          "(J)Z", (void*)Bitmap_hasMipMap },

-    {   "nativeSetHasMipMap",       "(JZ)V", (void*)Bitmap_setHasMipMap },

-    {   "nativeCreateFromParcel",

-        "(Landroid/os/Parcel;)Landroid/graphics/Bitmap;",

-        (void*)Bitmap_createFromParcel },

-    {   "nativeWriteToParcel",      "(JZILandroid/os/Parcel;)Z",

-        (void*)Bitmap_writeToParcel },

-    {   "nativeExtractAlpha",       "(JJ[I)Landroid/graphics/Bitmap;",

-        (void*)Bitmap_extractAlpha },

-    {   "nativeGenerationId",       "(J)I", (void*)Bitmap_getGenerationId },

-    {   "nativeGetPixel",           "(JIIZ)I", (void*)Bitmap_getPixel },

-    {   "nativeGetPixels",          "(J[IIIIIIIZ)V", (void*)Bitmap_getPixels },

-    {   "nativeSetPixel",           "(JIIIZ)V", (void*)Bitmap_setPixel },

-    {   "nativeSetPixels",          "(J[IIIIIIIZ)V", (void*)Bitmap_setPixels },

-    {   "nativeCopyPixelsToBuffer", "(JLjava/nio/Buffer;)V",

-                                            (void*)Bitmap_copyPixelsToBuffer },

-    {   "nativeCopyPixelsFromBuffer", "(JLjava/nio/Buffer;)V",

-                                            (void*)Bitmap_copyPixelsFromBuffer },

-    {   "nativeSameAs",             "(JJ)Z", (void*)Bitmap_sameAs },

-    {   "nativePrepareToDraw",      "(J)V", (void*)Bitmap_prepareToDraw },

-};

-

-#define kClassPathName  "android/graphics/Bitmap"

-

-int register_android_graphics_Bitmap(JNIEnv* env)

-{

-    return android::AndroidRuntime::registerNativeMethods(env, kClassPathName,

-                                gBitmapMethods, SK_ARRAY_COUNT(gBitmapMethods));

-}

+        return NULL;
+    }
+    return GraphicsJNI::createBitmap(env, new SkBitmap(result), allocator.getStorageObj(),
+            getPremulBitmapCreateFlags(isMutable), NULL, NULL);
+}
+
+static void Bitmap_destructor(JNIEnv* env, jobject, jlong bitmapHandle) {
+    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
+#ifdef USE_OPENGL_RENDERER
+    if (android::uirenderer::Caches::hasInstance()) {
+        android::uirenderer::Caches::getInstance().resourceCache.destructor(bitmap);
+        return;
+    }
+#endif // USE_OPENGL_RENDERER
+    delete bitmap;
+}
+
+static jboolean Bitmap_recycle(JNIEnv* env, jobject, jlong bitmapHandle) {
+    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
+#ifdef USE_OPENGL_RENDERER
+    if (android::uirenderer::Caches::hasInstance()) {
+        bool result;
+        result = android::uirenderer::Caches::getInstance().resourceCache.recycle(bitmap);
+        return result ? JNI_TRUE : JNI_FALSE;
+    }
+#endif // USE_OPENGL_RENDERER
+    bitmap->setPixels(NULL, NULL);
+    return JNI_TRUE;
+}
+
+static void Bitmap_reconfigure(JNIEnv* env, jobject clazz, jlong bitmapHandle,
+        jint width, jint height, jint configHandle, jint allocSize) {
+    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
+    SkBitmap::Config config = static_cast<SkBitmap::Config>(configHandle);
+    if (width * height * SkBitmap::ComputeBytesPerPixel(config) > allocSize) {
+        // done in native as there's no way to get BytesPerPixel in Java
+        doThrowIAE(env, "Bitmap not large enough to support new configuration");
+        return;
+    }
+    SkPixelRef* ref = bitmap->pixelRef();
+    SkSafeRef(ref);
+    bitmap->setConfig(config, width, height);
+    bitmap->setPixelRef(ref);
+
+    // notifyPixelsChanged will increment the generation ID even though the actual pixel data
+    // hasn't been touched. This signals the renderer that the bitmap (including width, height,
+    // and config) has changed.
+    ref->notifyPixelsChanged();
+    SkSafeUnref(ref);
+}
+
+// These must match the int values in Bitmap.java
+enum JavaEncodeFormat {
+    kJPEG_JavaEncodeFormat = 0,
+    kPNG_JavaEncodeFormat = 1,
+    kWEBP_JavaEncodeFormat = 2
+};
+
+static jboolean Bitmap_compress(JNIEnv* env, jobject clazz, jlong bitmapHandle,
+                                jint format, jint quality,
+                                jobject jstream, jbyteArray jstorage) {
+    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
+    SkImageEncoder::Type fm;
+
+    switch (format) {
+    case kJPEG_JavaEncodeFormat:
+        fm = SkImageEncoder::kJPEG_Type;
+        break;
+    case kPNG_JavaEncodeFormat:
+        fm = SkImageEncoder::kPNG_Type;
+        break;
+    case kWEBP_JavaEncodeFormat:
+        fm = SkImageEncoder::kWEBP_Type;
+        break;
+    default:
+        return JNI_FALSE;
+    }
+
+    bool success = false;
+    if (NULL != bitmap) {
+        SkAutoLockPixels alp(*bitmap);
+
+        if (NULL == bitmap->getPixels()) {
+            return JNI_FALSE;
+        }
+
+        SkWStream* strm = CreateJavaOutputStreamAdaptor(env, jstream, jstorage);
+        if (NULL == strm) {
+            return JNI_FALSE;
+        }
+
+        SkImageEncoder* encoder = SkImageEncoder::Create(fm);
+        if (NULL != encoder) {
+            success = encoder->encodeStream(strm, *bitmap, quality);
+            delete encoder;
+        }
+        delete strm;
+    }
+    return success ? JNI_TRUE : JNI_FALSE;
+}
+
+static void Bitmap_erase(JNIEnv* env, jobject, jlong bitmapHandle, jint color) {
+    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
+    bitmap->eraseColor(color);
+}
+
+static jint Bitmap_rowBytes(JNIEnv* env, jobject, jlong bitmapHandle) {
+    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
+    return static_cast<jint>(bitmap->rowBytes());
+}
+
+static jint Bitmap_config(JNIEnv* env, jobject, jlong bitmapHandle) {
+    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
+    return static_cast<jint>(bitmap->config());
+}
+
+static jint Bitmap_getGenerationId(JNIEnv* env, jobject, jlong bitmapHandle) {
+    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
+    return static_cast<jint>(bitmap->getGenerationID());
+}
+
+static jboolean Bitmap_hasAlpha(JNIEnv* env, jobject, jlong bitmapHandle) {
+    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
+    return !bitmap->isOpaque() ? JNI_TRUE : JNI_FALSE;
+}
+
+static void Bitmap_setAlphaAndPremultiplied(JNIEnv* env, jobject, jlong bitmapHandle,
+                                            jboolean hasAlpha, jboolean isPremul) {
+    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
+    if (!hasAlpha) {
+        bitmap->setAlphaType(kOpaque_SkAlphaType);
+    } else if (isPremul) {
+        bitmap->setAlphaType(kPremul_SkAlphaType);
+    } else {
+        bitmap->setAlphaType(kUnpremul_SkAlphaType);
+    }
+}
+
+static jboolean Bitmap_hasMipMap(JNIEnv* env, jobject, jlong bitmapHandle) {
+    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
+    return bitmap->hasHardwareMipMap() ? JNI_TRUE : JNI_FALSE;
+}
+
+static void Bitmap_setHasMipMap(JNIEnv* env, jobject, jlong bitmapHandle,
+                                jboolean hasMipMap) {
+    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
+    bitmap->setHasHardwareMipMap(hasMipMap);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static jobject Bitmap_createFromParcel(JNIEnv* env, jobject, jobject parcel) {
+    if (parcel == NULL) {
+        SkDebugf("-------- unparcel parcel is NULL\n");
+        return NULL;
+    }
+
+    android::Parcel* p = android::parcelForJavaObject(env, parcel);
+
+    const bool              isMutable = p->readInt32() != 0;
+    const SkBitmap::Config  config = (SkBitmap::Config)p->readInt32();
+    const int               width = p->readInt32();
+    const int               height = p->readInt32();
+    const int               rowBytes = p->readInt32();
+    const int               density = p->readInt32();
+
+    if (SkBitmap::kARGB_8888_Config != config &&
+            SkBitmap::kRGB_565_Config != config &&
+            SkBitmap::kARGB_4444_Config != config &&
+            SkBitmap::kIndex8_Config != config &&
+            SkBitmap::kA8_Config != config) {
+        SkDebugf("Bitmap_createFromParcel unknown config: %d\n", config);
+        return NULL;
+    }
+
+    SkBitmap* bitmap = new SkBitmap;
+
+    bitmap->setConfig(config, width, height, rowBytes);
+
+    SkColorTable* ctable = NULL;
+    if (config == SkBitmap::kIndex8_Config) {
+        int count = p->readInt32();
+        if (count > 0) {
+            size_t size = count * sizeof(SkPMColor);
+            const SkPMColor* src = (const SkPMColor*)p->readInplace(size);
+            ctable = new SkColorTable(src, count);
+        }
+    }
+
+    jbyteArray buffer = GraphicsJNI::allocateJavaPixelRef(env, bitmap, ctable);
+    if (NULL == buffer) {
+        SkSafeUnref(ctable);
+        delete bitmap;
+        return NULL;
+    }
+
+    SkSafeUnref(ctable);
+
+    size_t size = bitmap->getSize();
+
+    android::Parcel::ReadableBlob blob;
+    android::status_t status = p->readBlob(size, &blob);
+    if (status) {
+        doThrowRE(env, "Could not read bitmap from parcel blob.");
+        delete bitmap;
+        return NULL;
+    }
+
+    bitmap->lockPixels();
+    memcpy(bitmap->getPixels(), blob.data(), size);
+    bitmap->unlockPixels();
+
+    blob.release();
+
+    return GraphicsJNI::createBitmap(env, bitmap, buffer, getPremulBitmapCreateFlags(isMutable),
+            NULL, NULL, density);
+}
+
+static jboolean Bitmap_writeToParcel(JNIEnv* env, jobject,
+                                     jlong bitmapHandle,
+                                     jboolean isMutable, jint density,
+                                     jobject parcel) {
+    const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
+    if (parcel == NULL) {
+        SkDebugf("------- writeToParcel null parcel\n");
+        return JNI_FALSE;
+    }
+
+    android::Parcel* p = android::parcelForJavaObject(env, parcel);
+
+    p->writeInt32(isMutable);
+    p->writeInt32(bitmap->config());
+    p->writeInt32(bitmap->width());
+    p->writeInt32(bitmap->height());
+    p->writeInt32(bitmap->rowBytes());
+    p->writeInt32(density);
+
+    if (bitmap->config() == SkBitmap::kIndex8_Config) {
+        SkColorTable* ctable = bitmap->getColorTable();
+        if (ctable != NULL) {
+            int count = ctable->count();
+            p->writeInt32(count);
+            memcpy(p->writeInplace(count * sizeof(SkPMColor)),
+                   ctable->lockColors(), count * sizeof(SkPMColor));
+            ctable->unlockColors();
+        } else {
+            p->writeInt32(0);   // indicate no ctable
+        }
+    }
+
+    size_t size = bitmap->getSize();
+
+    android::Parcel::WritableBlob blob;
+    android::status_t status = p->writeBlob(size, &blob);
+    if (status) {
+        doThrowRE(env, "Could not write bitmap to parcel blob.");
+        return JNI_FALSE;
+    }
+
+    bitmap->lockPixels();
+    const void* pSrc =  bitmap->getPixels();
+    if (pSrc == NULL) {
+        memset(blob.data(), 0, size);
+    } else {
+        memcpy(blob.data(), pSrc, size);
+    }
+    bitmap->unlockPixels();
+
+    blob.release();
+    return JNI_TRUE;
+}
+
+static jobject Bitmap_extractAlpha(JNIEnv* env, jobject clazz,
+                                   jlong srcHandle, jlong paintHandle,
+                                   jintArray offsetXY) {
+    const SkBitmap* src = reinterpret_cast<SkBitmap*>(srcHandle);
+    const SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+    SkIPoint  offset;
+    SkBitmap* dst = new SkBitmap;
+    JavaPixelAllocator allocator(env);
+
+    src->extractAlpha(dst, paint, &allocator, &offset);
+    // If Skia can't allocate pixels for destination bitmap, it resets
+    // it, that is set its pixels buffer to NULL, and zero width and height.
+    if (dst->getPixels() == NULL && src->getPixels() != NULL) {
+        delete dst;
+        doThrowOOME(env, "failed to allocate pixels for alpha");
+        return NULL;
+    }
+    if (offsetXY != 0 && env->GetArrayLength(offsetXY) >= 2) {
+        int* array = env->GetIntArrayElements(offsetXY, NULL);
+        array[0] = offset.fX;
+        array[1] = offset.fY;
+        env->ReleaseIntArrayElements(offsetXY, array, 0);
+    }
+
+    return GraphicsJNI::createBitmap(env, dst, allocator.getStorageObj(),
+            getPremulBitmapCreateFlags(true), NULL, NULL);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static jint Bitmap_getPixel(JNIEnv* env, jobject, jlong bitmapHandle,
+        jint x, jint y, jboolean isPremultiplied) {
+    const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
+    SkAutoLockPixels alp(*bitmap);
+
+    ToColorProc proc = ChooseToColorProc(*bitmap, isPremultiplied);
+    if (NULL == proc) {
+        return 0;
+    }
+    const void* src = bitmap->getAddr(x, y);
+    if (NULL == src) {
+        return 0;
+    }
+
+    SkColor dst[1];
+    proc(dst, src, 1, bitmap->getColorTable());
+    return static_cast<jint>(dst[0]);
+}
+
+static void Bitmap_getPixels(JNIEnv* env, jobject, jlong bitmapHandle,
+        jintArray pixelArray, jint offset, jint stride,
+        jint x, jint y, jint width, jint height, jboolean isPremultiplied) {
+    const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
+    SkAutoLockPixels alp(*bitmap);
+
+    ToColorProc proc = ChooseToColorProc(*bitmap, isPremultiplied);
+    if (NULL == proc) {
+        return;
+    }
+    const void* src = bitmap->getAddr(x, y);
+    if (NULL == src) {
+        return;
+    }
+
+    SkColorTable* ctable = bitmap->getColorTable();
+    jint* dst = env->GetIntArrayElements(pixelArray, NULL);
+    SkColor* d = (SkColor*)dst + offset;
+    while (--height >= 0) {
+        proc(d, src, width, ctable);
+        d += stride;
+        src = (void*)((const char*)src + bitmap->rowBytes());
+    }
+    env->ReleaseIntArrayElements(pixelArray, dst, 0);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void Bitmap_setPixel(JNIEnv* env, jobject, jlong bitmapHandle,
+        jint x, jint y, jint colorHandle, jboolean isPremultiplied) {
+    const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
+    SkColor color = static_cast<SkColor>(colorHandle);
+    SkAutoLockPixels alp(*bitmap);
+    if (NULL == bitmap->getPixels()) {
+        return;
+    }
+
+    FromColorProc proc = ChooseFromColorProc(bitmap->config(), isPremultiplied);
+    if (NULL == proc) {
+        return;
+    }
+
+    proc(bitmap->getAddr(x, y), &color, 1, x, y);
+    bitmap->notifyPixelsChanged();
+}
+
+static void Bitmap_setPixels(JNIEnv* env, jobject, jlong bitmapHandle,
+        jintArray pixelArray, jint offset, jint stride,
+        jint x, jint y, jint width, jint height, jboolean isPremultiplied) {
+    const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
+    GraphicsJNI::SetPixels(env, pixelArray, offset, stride,
+            x, y, width, height, *bitmap, isPremultiplied);
+}
+
+static void Bitmap_copyPixelsToBuffer(JNIEnv* env, jobject,
+                                      jlong bitmapHandle, jobject jbuffer) {
+    const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
+    SkAutoLockPixels alp(*bitmap);
+    const void* src = bitmap->getPixels();
+
+    if (NULL != src) {
+        android::AutoBufferPointer abp(env, jbuffer, JNI_TRUE);
+
+        // the java side has already checked that buffer is large enough
+        memcpy(abp.pointer(), src, bitmap->getSize());
+    }
+}
+
+static void Bitmap_copyPixelsFromBuffer(JNIEnv* env, jobject,
+                                        jlong bitmapHandle, jobject jbuffer) {
+    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
+    SkAutoLockPixels alp(*bitmap);
+    void* dst = bitmap->getPixels();
+
+    if (NULL != dst) {
+        android::AutoBufferPointer abp(env, jbuffer, JNI_FALSE);
+        // the java side has already checked that buffer is large enough
+        memcpy(dst, abp.pointer(), bitmap->getSize());
+        bitmap->notifyPixelsChanged();
+    }
+}
+
+static jboolean Bitmap_sameAs(JNIEnv* env, jobject, jlong bm0Handle,
+                              jlong bm1Handle) {
+    const SkBitmap* bm0 = reinterpret_cast<SkBitmap*>(bm0Handle);
+    const SkBitmap* bm1 = reinterpret_cast<SkBitmap*>(bm1Handle);
+    if (bm0->width() != bm1->width() ||
+        bm0->height() != bm1->height() ||
+        bm0->config() != bm1->config()) {
+        return JNI_FALSE;
+    }
+
+    SkAutoLockPixels alp0(*bm0);
+    SkAutoLockPixels alp1(*bm1);
+
+    // if we can't load the pixels, return false
+    if (NULL == bm0->getPixels() || NULL == bm1->getPixels()) {
+        return JNI_FALSE;
+    }
+
+    if (bm0->config() == SkBitmap::kIndex8_Config) {
+        SkColorTable* ct0 = bm0->getColorTable();
+        SkColorTable* ct1 = bm1->getColorTable();
+        if (NULL == ct0 || NULL == ct1) {
+            return JNI_FALSE;
+        }
+        if (ct0->count() != ct1->count()) {
+            return JNI_FALSE;
+        }
+
+        SkAutoLockColors alc0(ct0);
+        SkAutoLockColors alc1(ct1);
+        const size_t size = ct0->count() * sizeof(SkPMColor);
+        if (memcmp(alc0.colors(), alc1.colors(), size) != 0) {
+            return JNI_FALSE;
+        }
+    }
+
+    // now compare each scanline. We can't do the entire buffer at once,
+    // since we don't care about the pixel values that might extend beyond
+    // the width (since the scanline might be larger than the logical width)
+    const int h = bm0->height();
+    const size_t size = bm0->width() * bm0->bytesPerPixel();
+    for (int y = 0; y < h; y++) {
+        if (memcmp(bm0->getAddr(0, y), bm1->getAddr(0, y), size) != 0) {
+            return JNI_FALSE;
+        }
+    }
+    return JNI_TRUE;
+}
+
+static void Bitmap_prepareToDraw(JNIEnv* env, jobject, jlong bitmapHandle) {
+    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
+    bitmap->lockPixels();
+    bitmap->unlockPixels();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include <android_runtime/AndroidRuntime.h>
+
+static JNINativeMethod gBitmapMethods[] = {
+    {   "nativeCreate",             "([IIIIIIZ)Landroid/graphics/Bitmap;",
+        (void*)Bitmap_creator },
+    {   "nativeCopy",               "(JIZ)Landroid/graphics/Bitmap;",
+        (void*)Bitmap_copy },
+    {   "nativeDestructor",         "(J)V", (void*)Bitmap_destructor },
+    {   "nativeRecycle",            "(J)Z", (void*)Bitmap_recycle },
+    {   "nativeReconfigure",        "(JIIII)V", (void*)Bitmap_reconfigure },
+    {   "nativeCompress",           "(JIILjava/io/OutputStream;[B)Z",
+        (void*)Bitmap_compress },
+    {   "nativeErase",              "(JI)V", (void*)Bitmap_erase },
+    {   "nativeRowBytes",           "(J)I", (void*)Bitmap_rowBytes },
+    {   "nativeConfig",             "(J)I", (void*)Bitmap_config },
+    {   "nativeHasAlpha",           "(J)Z", (void*)Bitmap_hasAlpha },
+    {   "nativeSetAlphaAndPremultiplied", "(JZZ)V", (void*)Bitmap_setAlphaAndPremultiplied},
+    {   "nativeHasMipMap",          "(J)Z", (void*)Bitmap_hasMipMap },
+    {   "nativeSetHasMipMap",       "(JZ)V", (void*)Bitmap_setHasMipMap },
+    {   "nativeCreateFromParcel",
+        "(Landroid/os/Parcel;)Landroid/graphics/Bitmap;",
+        (void*)Bitmap_createFromParcel },
+    {   "nativeWriteToParcel",      "(JZILandroid/os/Parcel;)Z",
+        (void*)Bitmap_writeToParcel },
+    {   "nativeExtractAlpha",       "(JJ[I)Landroid/graphics/Bitmap;",
+        (void*)Bitmap_extractAlpha },
+    {   "nativeGenerationId",       "(J)I", (void*)Bitmap_getGenerationId },
+    {   "nativeGetPixel",           "(JIIZ)I", (void*)Bitmap_getPixel },
+    {   "nativeGetPixels",          "(J[IIIIIIIZ)V", (void*)Bitmap_getPixels },
+    {   "nativeSetPixel",           "(JIIIZ)V", (void*)Bitmap_setPixel },
+    {   "nativeSetPixels",          "(J[IIIIIIIZ)V", (void*)Bitmap_setPixels },
+    {   "nativeCopyPixelsToBuffer", "(JLjava/nio/Buffer;)V",
+                                            (void*)Bitmap_copyPixelsToBuffer },
+    {   "nativeCopyPixelsFromBuffer", "(JLjava/nio/Buffer;)V",
+                                            (void*)Bitmap_copyPixelsFromBuffer },
+    {   "nativeSameAs",             "(JJ)Z", (void*)Bitmap_sameAs },
+    {   "nativePrepareToDraw",      "(J)V", (void*)Bitmap_prepareToDraw },
+};
+
+#define kClassPathName  "android/graphics/Bitmap"
+
+int register_android_graphics_Bitmap(JNIEnv* env)
+{
+    return android::AndroidRuntime::registerNativeMethods(env, kClassPathName,
+                                gBitmapMethods, SK_ARRAY_COUNT(gBitmapMethods));
+}
diff --git a/core/jni/android/graphics/CanvasProperty.cpp b/core/jni/android/graphics/CanvasProperty.cpp
index 70e2db5..cfa9cd8 100644
--- a/core/jni/android/graphics/CanvasProperty.cpp
+++ b/core/jni/android/graphics/CanvasProperty.cpp
@@ -18,7 +18,7 @@
 #include "GraphicsJNI.h"
 #include <android_runtime/AndroidRuntime.h>
 
-#include <utils/VirtualLightRefBase.h>
+#include <utils/RefBase.h>
 #include <CanvasProperty.h>
 
 namespace android {
@@ -27,22 +27,13 @@
 
 #ifdef USE_OPENGL_RENDERER
 
-static jlong incRef(VirtualLightRefBase* ptr) {
-    ptr->incStrong(0);
-    return reinterpret_cast<jlong>(ptr);
-}
-
 static jlong createFloat(JNIEnv* env, jobject clazz, jfloat initialValue) {
-    return incRef(new CanvasPropertyPrimitive(initialValue));
+    return reinterpret_cast<jlong>(new CanvasPropertyPrimitive(initialValue));
 }
 
 static jlong createPaint(JNIEnv* env, jobject clazz, jlong paintPtr) {
     const SkPaint* paint = reinterpret_cast<const SkPaint*>(paintPtr);
-    return incRef(new CanvasPropertyPaint(*paint));
-}
-
-static void unref(JNIEnv* env, jobject clazz, jlong containerPtr) {
-    reinterpret_cast<VirtualLightRefBase*>(containerPtr)->decStrong(0);
+    return reinterpret_cast<jlong>(new CanvasPropertyPaint(*paint));
 }
 
 #endif
@@ -57,7 +48,6 @@
 #ifdef USE_OPENGL_RENDERER
     { "nCreateFloat", "(F)J", (void*) createFloat },
     { "nCreatePaint", "(J)J", (void*) createPaint },
-    { "nUnref", "(J)V", (void*) unref },
 #endif
 };
 
diff --git a/core/jni/android_view_RenderNodeAnimator.cpp b/core/jni/android_view_RenderNodeAnimator.cpp
index 3be013b..5733f60 100644
--- a/core/jni/android_view_RenderNodeAnimator.cpp
+++ b/core/jni/android_view_RenderNodeAnimator.cpp
@@ -101,7 +101,6 @@
     RenderPropertyAnimator::DeltaValueType deltaType = toDeltaType(deltaTypeRaw);
 
     BaseAnimator* animator = new RenderPropertyAnimator(property, deltaType, deltaValue);
-    animator->incStrong(0);
     animator->setListener(new AnimationListenerBridge(env, weakThis));
     return reinterpret_cast<jlong>( animator );
 }
@@ -111,7 +110,6 @@
     RenderPropertyAnimator::DeltaValueType deltaType = toDeltaType(deltaTypeRaw);
     CanvasPropertyPrimitive* canvasProperty = reinterpret_cast<CanvasPropertyPrimitive*>(canvasPropertyPtr);
     BaseAnimator* animator = new CanvasPropertyPrimitiveAnimator(canvasProperty, deltaType, deltaValue);
-    animator->incStrong(0);
     animator->setListener(new AnimationListenerBridge(env, weakThis));
     return reinterpret_cast<jlong>( animator );
 }
@@ -124,7 +122,6 @@
     CanvasPropertyPaintAnimator::PaintField paintField = toPaintField(paintFieldRaw);
     BaseAnimator* animator = new CanvasPropertyPaintAnimator(
             canvasProperty, paintField, deltaType, deltaValue);
-    animator->incStrong(0);
     animator->setListener(new AnimationListenerBridge(env, weakThis));
     return reinterpret_cast<jlong>( animator );
 }
@@ -135,9 +132,15 @@
     animator->setDuration(duration);
 }
 
-static void unref(JNIEnv* env, jobject clazz, jlong objPtr) {
-    VirtualLightRefBase* obj = reinterpret_cast<VirtualLightRefBase*>(objPtr);
-    obj->decStrong(0);
+static jint getDuration(JNIEnv* env, jobject clazz, jlong animatorPtr) {
+    BaseAnimator* animator = reinterpret_cast<BaseAnimator*>(animatorPtr);
+    return static_cast<jint>(animator->duration());
+}
+
+static void setInterpolator(JNIEnv* env, jobject clazz, jlong animatorPtr, jlong interpolatorPtr) {
+    BaseAnimator* animator = reinterpret_cast<BaseAnimator*>(animatorPtr);
+    Interpolator* interpolator = reinterpret_cast<Interpolator*>(interpolatorPtr);
+    animator->setInterpolator(interpolator);
 }
 
 #endif
@@ -154,7 +157,8 @@
     { "nCreateCanvasPropertyFloatAnimator", "(Ljava/lang/ref/WeakReference;JIF)J", (void*) createCanvasPropertyFloatAnimator },
     { "nCreateCanvasPropertyPaintAnimator", "(Ljava/lang/ref/WeakReference;JIIF)J", (void*) createCanvasPropertyPaintAnimator },
     { "nSetDuration", "(JI)V", (void*) setDuration },
-    { "nUnref", "(J)V", (void*) unref },
+    { "nGetDuration", "(J)I", (void*) getDuration },
+    { "nSetInterpolator", "(JJ)V", (void*) setInterpolator },
 #endif
 };
 
diff --git a/core/jni/com_android_internal_util_VirtualRefBasePtr.cpp b/core/jni/com_android_internal_util_VirtualRefBasePtr.cpp
new file mode 100644
index 0000000..ce6f207
--- /dev/null
+++ b/core/jni/com_android_internal_util_VirtualRefBasePtr.cpp
@@ -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.
+ */
+
+#include "jni.h"
+#include <nativehelper/JNIHelp.h>
+#include <android_runtime/AndroidRuntime.h>
+
+namespace android {
+
+static void incStrong(JNIEnv* env, jobject clazz, jlong objPtr) {
+    VirtualLightRefBase* obj = reinterpret_cast<VirtualLightRefBase*>(objPtr);
+    obj->incStrong(0);
+}
+
+static void decStrong(JNIEnv* env, jobject clazz, jlong objPtr) {
+    VirtualLightRefBase* obj = reinterpret_cast<VirtualLightRefBase*>(objPtr);
+    obj->decStrong(0);
+}
+
+// ----------------------------------------------------------------------------
+// JNI Glue
+// ----------------------------------------------------------------------------
+
+const char* const kClassPathName = "com/android/internal/util/VirtualRefBasePtr";
+
+static JNINativeMethod gMethods[] = {
+    { "nIncStrong", "(J)V", (void*) incStrong },
+    { "nDecStrong", "(J)V", (void*) decStrong },
+};
+
+int register_com_android_internal_util_VirtualRefBasePtr(JNIEnv* env) {
+    return AndroidRuntime::registerNativeMethods(env, kClassPathName, gMethods, NELEM(gMethods));
+}
+
+
+} // namespace android
diff --git a/core/jni/com_android_internal_view_animation_NativeInterpolatorFactoryHelper.cpp b/core/jni/com_android_internal_view_animation_NativeInterpolatorFactoryHelper.cpp
new file mode 100644
index 0000000..704e1be
--- /dev/null
+++ b/core/jni/com_android_internal_view_animation_NativeInterpolatorFactoryHelper.cpp
@@ -0,0 +1,105 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "OpenGLRenderer"
+
+#include "jni.h"
+#include <nativehelper/JNIHelp.h>
+#include <android_runtime/AndroidRuntime.h>
+
+#include <Interpolator.h>
+
+namespace android {
+
+using namespace uirenderer;
+
+#ifdef USE_OPENGL_RENDERER
+
+static jlong createAccelerateDecelerateInterpolator(JNIEnv* env, jobject clazz) {
+    return reinterpret_cast<jlong>(new AccelerateDecelerateInterpolator());
+}
+
+static jlong createAccelerateInterpolator(JNIEnv* env, jobject clazz, jfloat factor) {
+    return reinterpret_cast<jlong>(new AccelerateInterpolator(factor));
+}
+
+static jlong createAnticipateInterpolator(JNIEnv* env, jobject clazz, jfloat tension) {
+    return reinterpret_cast<jlong>(new AnticipateInterpolator(tension));
+}
+
+static jlong createAnticipateOvershootInterpolator(JNIEnv* env, jobject clazz, jfloat tension) {
+    return reinterpret_cast<jlong>(new AnticipateOvershootInterpolator(tension));
+}
+
+static jlong createBounceInterpolator(JNIEnv* env, jobject clazz) {
+    return reinterpret_cast<jlong>(new BounceInterpolator());
+}
+
+static jlong createCycleInterpolator(JNIEnv* env, jobject clazz, jfloat cycles) {
+    return reinterpret_cast<jlong>(new CycleInterpolator(cycles));
+}
+
+static jlong createDecelerateInterpolator(JNIEnv* env, jobject clazz, jfloat factor) {
+    return reinterpret_cast<jlong>(new DecelerateInterpolator(factor));
+}
+
+static jlong createLinearInterpolator(JNIEnv* env, jobject clazz) {
+    return reinterpret_cast<jlong>(new LinearInterpolator());
+}
+
+static jlong createOvershootInterpolator(JNIEnv* env, jobject clazz, jfloat tension) {
+    return reinterpret_cast<jlong>(new OvershootInterpolator(tension));
+}
+
+static jlong createLutInterpolator(JNIEnv* env, jobject clazz, jfloatArray jlut) {
+    jsize len = env->GetArrayLength(jlut);
+    if (len <= 0) {
+        return 0;
+    }
+    float* lut = new float[len];
+    env->GetFloatArrayRegion(jlut, 0, len, lut);
+    return reinterpret_cast<jlong>(new LUTInterpolator(lut, len));
+}
+
+#endif
+
+// ----------------------------------------------------------------------------
+// JNI Glue
+// ----------------------------------------------------------------------------
+
+const char* const kClassPathName = "com/android/internal/view/animation/NativeInterpolatorFactoryHelper";
+
+static JNINativeMethod gMethods[] = {
+#ifdef USE_OPENGL_RENDERER
+    { "createAccelerateDecelerateInterpolator", "()J", (void*) createAccelerateDecelerateInterpolator },
+    { "createAccelerateInterpolator", "(F)J", (void*) createAccelerateInterpolator },
+    { "createAnticipateInterpolator", "(F)J", (void*) createAnticipateInterpolator },
+    { "createAnticipateOvershootInterpolator", "(F)J", (void*) createAnticipateOvershootInterpolator },
+    { "createBounceInterpolator", "()J", (void*) createBounceInterpolator },
+    { "createCycleInterpolator", "(F)J", (void*) createCycleInterpolator },
+    { "createDecelerateInterpolator", "(F)J", (void*) createDecelerateInterpolator },
+    { "createLinearInterpolator", "()J", (void*) createLinearInterpolator },
+    { "createOvershootInterpolator", "(F)J", (void*) createOvershootInterpolator },
+    { "createLutInterpolator", "([F)J", (void*) createLutInterpolator },
+#endif
+};
+
+int register_com_android_internal_view_animation_NativeInterpolatorFactoryHelper(JNIEnv* env) {
+    return AndroidRuntime::registerNativeMethods(env, kClassPathName, gMethods, NELEM(gMethods));
+}
+
+
+} // namespace android
diff --git a/core/res/res/drawable-hdpi/btn_check_anim_00000_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_check_anim_00000_qntm_alpha.png
new file mode 100644
index 0000000..1880a15
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_anim_00000_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_anim_00001_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_check_anim_00001_qntm_alpha.png
new file mode 100644
index 0000000..aecb4d2
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_anim_00001_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_anim_00002_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_check_anim_00002_qntm_alpha.png
new file mode 100644
index 0000000..8401f91
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_anim_00002_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_anim_00003_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_check_anim_00003_qntm_alpha.png
new file mode 100644
index 0000000..5832865
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_anim_00003_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_anim_00004_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_check_anim_00004_qntm_alpha.png
new file mode 100644
index 0000000..6d14962
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_anim_00004_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_anim_00005_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_check_anim_00005_qntm_alpha.png
new file mode 100644
index 0000000..aee057c
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_anim_00005_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_anim_00006_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_check_anim_00006_qntm_alpha.png
new file mode 100644
index 0000000..fb5801e
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_anim_00006_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_anim_00007_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_check_anim_00007_qntm_alpha.png
new file mode 100644
index 0000000..fdb5271
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_anim_00007_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_anim_00008_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_check_anim_00008_qntm_alpha.png
new file mode 100644
index 0000000..b8c7397
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_anim_00008_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_anim_00009_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_check_anim_00009_qntm_alpha.png
new file mode 100644
index 0000000..d0395a8
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_anim_00009_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_anim_00010_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_check_anim_00010_qntm_alpha.png
new file mode 100644
index 0000000..59bb437
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_anim_00010_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_anim_00011_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_check_anim_00011_qntm_alpha.png
new file mode 100644
index 0000000..c053b90
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_anim_00011_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_anim_00012_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_check_anim_00012_qntm_alpha.png
new file mode 100644
index 0000000..eb30a79
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_anim_00012_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_anim_00013_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_check_anim_00013_qntm_alpha.png
new file mode 100644
index 0000000..1af0bff
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_anim_00013_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_anim_00014_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_check_anim_00014_qntm_alpha.png
new file mode 100644
index 0000000..3b36e7d
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_anim_00014_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_check_anim_00015_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_check_anim_00015_qntm_alpha.png
new file mode 100644
index 0000000..c12d20a
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_check_anim_00015_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_anim_00000_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_radio_anim_00000_qntm_alpha.png
new file mode 100644
index 0000000..882365b
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_anim_00000_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_anim_00001_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_radio_anim_00001_qntm_alpha.png
new file mode 100644
index 0000000..f6c7094
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_anim_00001_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_anim_00002_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_radio_anim_00002_qntm_alpha.png
new file mode 100644
index 0000000..0e326c9
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_anim_00002_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_anim_00003_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_radio_anim_00003_qntm_alpha.png
new file mode 100644
index 0000000..8bf1170
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_anim_00003_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_anim_00004_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_radio_anim_00004_qntm_alpha.png
new file mode 100644
index 0000000..cedb66e
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_anim_00004_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_anim_00005_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_radio_anim_00005_qntm_alpha.png
new file mode 100644
index 0000000..257d7ba
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_anim_00005_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_anim_00006_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_radio_anim_00006_qntm_alpha.png
new file mode 100644
index 0000000..e07b36e
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_anim_00006_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_anim_00007_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_radio_anim_00007_qntm_alpha.png
new file mode 100644
index 0000000..ef94200
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_anim_00007_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_anim_00008_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_radio_anim_00008_qntm_alpha.png
new file mode 100644
index 0000000..ad67004
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_anim_00008_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_anim_00009_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_radio_anim_00009_qntm_alpha.png
new file mode 100644
index 0000000..50796e2
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_anim_00009_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_anim_00010_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_radio_anim_00010_qntm_alpha.png
new file mode 100644
index 0000000..ba7be9e
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_anim_00010_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_anim_00011_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_radio_anim_00011_qntm_alpha.png
new file mode 100644
index 0000000..bdbfe78
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_anim_00011_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_anim_00012_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_radio_anim_00012_qntm_alpha.png
new file mode 100644
index 0000000..fe89951
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_anim_00012_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_anim_00013_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_radio_anim_00013_qntm_alpha.png
new file mode 100644
index 0000000..840c88f
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_anim_00013_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_anim_00014_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_radio_anim_00014_qntm_alpha.png
new file mode 100644
index 0000000..621d1d2
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_anim_00014_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_radio_anim_00015_qntm_alpha.png b/core/res/res/drawable-hdpi/btn_radio_anim_00015_qntm_alpha.png
new file mode 100644
index 0000000..fd8be89
--- /dev/null
+++ b/core/res/res/drawable-hdpi/btn_radio_anim_00015_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/scrubber_control_off_qntm_alpha.png b/core/res/res/drawable-hdpi/scrubber_control_off_qntm_alpha.png
index f1023ea..5a99528 100644
--- a/core/res/res/drawable-hdpi/scrubber_control_off_qntm_alpha.png
+++ b/core/res/res/drawable-hdpi/scrubber_control_off_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/scrubber_control_on_qntm_alpha.png b/core/res/res/drawable-hdpi/scrubber_control_on_qntm_alpha.png
index 15ceeee..79de664 100644
--- a/core/res/res/drawable-hdpi/scrubber_control_on_qntm_alpha.png
+++ b/core/res/res/drawable-hdpi/scrubber_control_on_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/switch_off_qntm_alpha.9.png b/core/res/res/drawable-hdpi/switch_off_qntm_alpha.9.png
index 90b1498..73e8f1c 100644
--- a/core/res/res/drawable-hdpi/switch_off_qntm_alpha.9.png
+++ b/core/res/res/drawable-hdpi/switch_off_qntm_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/switch_on_qntm_alpha.9.png b/core/res/res/drawable-hdpi/switch_on_qntm_alpha.9.png
index b535758..ff6affe 100644
--- a/core/res/res/drawable-hdpi/switch_on_qntm_alpha.9.png
+++ b/core/res/res/drawable-hdpi/switch_on_qntm_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_anim_00000_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_check_anim_00000_qntm_alpha.png
new file mode 100644
index 0000000..0f44ff9
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_anim_00000_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_anim_00001_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_check_anim_00001_qntm_alpha.png
new file mode 100644
index 0000000..9d5dda0
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_anim_00001_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_anim_00002_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_check_anim_00002_qntm_alpha.png
new file mode 100644
index 0000000..e4ce802
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_anim_00002_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_anim_00003_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_check_anim_00003_qntm_alpha.png
new file mode 100644
index 0000000..d1806ac
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_anim_00003_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_anim_00004_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_check_anim_00004_qntm_alpha.png
new file mode 100644
index 0000000..ab9315b
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_anim_00004_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_anim_00005_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_check_anim_00005_qntm_alpha.png
new file mode 100644
index 0000000..46e90e6
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_anim_00005_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_anim_00006_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_check_anim_00006_qntm_alpha.png
new file mode 100644
index 0000000..e8c56ff
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_anim_00006_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_anim_00007_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_check_anim_00007_qntm_alpha.png
new file mode 100644
index 0000000..59dcb7e
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_anim_00007_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_anim_00008_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_check_anim_00008_qntm_alpha.png
new file mode 100644
index 0000000..e9bd4a2
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_anim_00008_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_anim_00009_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_check_anim_00009_qntm_alpha.png
new file mode 100644
index 0000000..1d05037
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_anim_00009_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_anim_00010_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_check_anim_00010_qntm_alpha.png
new file mode 100644
index 0000000..91b40de
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_anim_00010_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_anim_00011_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_check_anim_00011_qntm_alpha.png
new file mode 100644
index 0000000..c531cab
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_anim_00011_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_anim_00012_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_check_anim_00012_qntm_alpha.png
new file mode 100644
index 0000000..11bb387
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_anim_00012_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_anim_00013_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_check_anim_00013_qntm_alpha.png
new file mode 100644
index 0000000..8843210
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_anim_00013_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_anim_00014_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_check_anim_00014_qntm_alpha.png
new file mode 100644
index 0000000..6ff2f3d
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_anim_00014_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_anim_00015_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_check_anim_00015_qntm_alpha.png
new file mode 100644
index 0000000..a03c1e2
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_check_anim_00015_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_anim_00000_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_radio_anim_00000_qntm_alpha.png
new file mode 100644
index 0000000..0a22e1a
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_anim_00000_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_anim_00001_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_radio_anim_00001_qntm_alpha.png
new file mode 100644
index 0000000..2e2469c
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_anim_00001_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_anim_00002_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_radio_anim_00002_qntm_alpha.png
new file mode 100644
index 0000000..c1054d9
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_anim_00002_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_anim_00003_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_radio_anim_00003_qntm_alpha.png
new file mode 100644
index 0000000..cf8d80a
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_anim_00003_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_anim_00004_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_radio_anim_00004_qntm_alpha.png
new file mode 100644
index 0000000..9d9e870
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_anim_00004_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_anim_00005_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_radio_anim_00005_qntm_alpha.png
new file mode 100644
index 0000000..1bad701
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_anim_00005_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_anim_00006_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_radio_anim_00006_qntm_alpha.png
new file mode 100644
index 0000000..a84a54f
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_anim_00006_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_anim_00007_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_radio_anim_00007_qntm_alpha.png
new file mode 100644
index 0000000..4d8050b
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_anim_00007_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_anim_00008_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_radio_anim_00008_qntm_alpha.png
new file mode 100644
index 0000000..374172c
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_anim_00008_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_anim_00009_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_radio_anim_00009_qntm_alpha.png
new file mode 100644
index 0000000..233036e
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_anim_00009_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_anim_00010_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_radio_anim_00010_qntm_alpha.png
new file mode 100644
index 0000000..61d9b58
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_anim_00010_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_anim_00011_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_radio_anim_00011_qntm_alpha.png
new file mode 100644
index 0000000..274e983
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_anim_00011_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_anim_00012_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_radio_anim_00012_qntm_alpha.png
new file mode 100644
index 0000000..acf16e5
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_anim_00012_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_anim_00013_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_radio_anim_00013_qntm_alpha.png
new file mode 100644
index 0000000..ee48241
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_anim_00013_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_anim_00014_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_radio_anim_00014_qntm_alpha.png
new file mode 100644
index 0000000..dbbb736
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_anim_00014_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_radio_anim_00015_qntm_alpha.png b/core/res/res/drawable-mdpi/btn_radio_anim_00015_qntm_alpha.png
new file mode 100644
index 0000000..bcabd0d
--- /dev/null
+++ b/core/res/res/drawable-mdpi/btn_radio_anim_00015_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/scrubber_control_off_qntm_alpha.png b/core/res/res/drawable-mdpi/scrubber_control_off_qntm_alpha.png
index 1833704..e40cba8 100644
--- a/core/res/res/drawable-mdpi/scrubber_control_off_qntm_alpha.png
+++ b/core/res/res/drawable-mdpi/scrubber_control_off_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/scrubber_control_on_qntm_alpha.png b/core/res/res/drawable-mdpi/scrubber_control_on_qntm_alpha.png
index e64d3f2..437a3e3 100644
--- a/core/res/res/drawable-mdpi/scrubber_control_on_qntm_alpha.png
+++ b/core/res/res/drawable-mdpi/scrubber_control_on_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/switch_off_qntm_alpha.9.png b/core/res/res/drawable-mdpi/switch_off_qntm_alpha.9.png
index ffd6c39..8949b52 100644
--- a/core/res/res/drawable-mdpi/switch_off_qntm_alpha.9.png
+++ b/core/res/res/drawable-mdpi/switch_off_qntm_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/switch_on_qntm_alpha.9.png b/core/res/res/drawable-mdpi/switch_on_qntm_alpha.9.png
index 15faff0..d727683 100644
--- a/core/res/res/drawable-mdpi/switch_on_qntm_alpha.9.png
+++ b/core/res/res/drawable-mdpi/switch_on_qntm_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-nodpi/platlogo.xml b/core/res/res/drawable-nodpi/platlogo.xml
index 8eb00fa..668cff7 100644
--- a/core/res/res/drawable-nodpi/platlogo.xml
+++ b/core/res/res/drawable-nodpi/platlogo.xml
@@ -18,22 +18,21 @@
 
     <viewport android:viewportHeight="25" android:viewportWidth="25" />
 
-    <group>
-        <path
-            android:name="shadow"
-            android:pathData="m12,2.5 a11,11 0 1,0 1,0
-            M6.5,7.5
-            l5,0 l0,7 l7,0 l0,5 l-12,0 z"
-            android:fill="#40000000"
-            />
-        <path
-            android:name="circle-L-ranch"
-            android:pathData="m12,1.5 a11,11 0 1,0 1,0
-            M6.5,6.5
-            l5,0 l0,7 l7,0 l0,5 l-12,0 z"
-            android:fill="#FFFFFF40"
-            />
-    </group>
+    <path
+        android:name="shadow"
+        android:pathData="m12,2.5 a11,11 0 1,0 1,0
+        M6.5,7.5
+        l5,0 l0,7 l7,0 l0,5 l-12,0 z"
+        android:fill="#40000000"
+        />
+    <path
+        android:name="circle-L-ranch"
+        android:pathData="m12,1.5 a11,11 0 1,0 1,0
+        M6.5,6.5
+        l5,0 l0,7 l7,0 l0,5 l-12,0 z"
+        android:fill="#FFFFFF40"
+        />
+
 </vector>
 
 
diff --git a/core/res/res/drawable-nodpi/stat_sys_adb.xml b/core/res/res/drawable-nodpi/stat_sys_adb.xml
index 37df348..b8ddb77 100644
--- a/core/res/res/drawable-nodpi/stat_sys_adb.xml
+++ b/core/res/res/drawable-nodpi/stat_sys_adb.xml
@@ -18,13 +18,13 @@
 
     <viewport android:viewportHeight="25" android:viewportWidth="25" />
 
-    <group>
-        <path
-            android:name="adb"
-            android:pathData="m3,3l8,0l0,11l11,0l0,8l-18,0z"
-            android:fill="#FFFFFFFF"
-            />
-    </group>
+
+    <path
+        android:name="adb"
+        android:pathData="m3,3l8,0l0,11l11,0l0,8l-19,0z"
+        android:fill="#FFFFFFFF"
+        />
+
 </vector>
 
 
diff --git a/core/res/res/drawable-xhdpi/btn_check_anim_00000_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_check_anim_00000_qntm_alpha.png
new file mode 100644
index 0000000..25500e8
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_check_anim_00000_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_anim_00001_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_check_anim_00001_qntm_alpha.png
new file mode 100644
index 0000000..b136e25
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_check_anim_00001_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_anim_00002_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_check_anim_00002_qntm_alpha.png
new file mode 100644
index 0000000..6a94e30
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_check_anim_00002_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_anim_00003_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_check_anim_00003_qntm_alpha.png
new file mode 100644
index 0000000..d386421
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_check_anim_00003_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_anim_00004_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_check_anim_00004_qntm_alpha.png
new file mode 100644
index 0000000..c811385
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_check_anim_00004_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_anim_00005_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_check_anim_00005_qntm_alpha.png
new file mode 100644
index 0000000..58b3267
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_check_anim_00005_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_anim_00006_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_check_anim_00006_qntm_alpha.png
new file mode 100644
index 0000000..0659e72
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_check_anim_00006_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_anim_00007_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_check_anim_00007_qntm_alpha.png
new file mode 100644
index 0000000..b4227d1
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_check_anim_00007_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_anim_00008_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_check_anim_00008_qntm_alpha.png
new file mode 100644
index 0000000..714ef00
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_check_anim_00008_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_anim_00009_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_check_anim_00009_qntm_alpha.png
new file mode 100644
index 0000000..139595b
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_check_anim_00009_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_anim_00010_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_check_anim_00010_qntm_alpha.png
new file mode 100644
index 0000000..4491107
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_check_anim_00010_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_anim_00011_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_check_anim_00011_qntm_alpha.png
new file mode 100644
index 0000000..20eb752
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_check_anim_00011_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_anim_00012_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_check_anim_00012_qntm_alpha.png
new file mode 100644
index 0000000..532c9f2
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_check_anim_00012_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_anim_00013_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_check_anim_00013_qntm_alpha.png
new file mode 100644
index 0000000..0d78a32
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_check_anim_00013_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_anim_00014_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_check_anim_00014_qntm_alpha.png
new file mode 100644
index 0000000..af29678
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_check_anim_00014_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_check_anim_00015_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_check_anim_00015_qntm_alpha.png
new file mode 100644
index 0000000..23eb9e3
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_check_anim_00015_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_anim_00000_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_radio_anim_00000_qntm_alpha.png
new file mode 100644
index 0000000..cd11e14
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_radio_anim_00000_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_anim_00001_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_radio_anim_00001_qntm_alpha.png
new file mode 100644
index 0000000..b10db83
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_radio_anim_00001_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_anim_00002_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_radio_anim_00002_qntm_alpha.png
new file mode 100644
index 0000000..efeb6fb
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_radio_anim_00002_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_anim_00003_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_radio_anim_00003_qntm_alpha.png
new file mode 100644
index 0000000..83080af
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_radio_anim_00003_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_anim_00004_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_radio_anim_00004_qntm_alpha.png
new file mode 100644
index 0000000..b9cc322
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_radio_anim_00004_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_anim_00005_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_radio_anim_00005_qntm_alpha.png
new file mode 100644
index 0000000..3b5f9c4
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_radio_anim_00005_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_anim_00006_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_radio_anim_00006_qntm_alpha.png
new file mode 100644
index 0000000..58c93db
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_radio_anim_00006_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_anim_00007_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_radio_anim_00007_qntm_alpha.png
new file mode 100644
index 0000000..0f1d010
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_radio_anim_00007_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_anim_00008_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_radio_anim_00008_qntm_alpha.png
new file mode 100644
index 0000000..05a7a0f
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_radio_anim_00008_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_anim_00009_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_radio_anim_00009_qntm_alpha.png
new file mode 100644
index 0000000..9345035
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_radio_anim_00009_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_anim_00010_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_radio_anim_00010_qntm_alpha.png
new file mode 100644
index 0000000..5f149b7
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_radio_anim_00010_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_anim_00011_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_radio_anim_00011_qntm_alpha.png
new file mode 100644
index 0000000..191f369
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_radio_anim_00011_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_anim_00012_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_radio_anim_00012_qntm_alpha.png
new file mode 100644
index 0000000..44e08e6
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_radio_anim_00012_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_anim_00013_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_radio_anim_00013_qntm_alpha.png
new file mode 100644
index 0000000..5a9dfa0
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_radio_anim_00013_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_anim_00014_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_radio_anim_00014_qntm_alpha.png
new file mode 100644
index 0000000..ee921c6
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_radio_anim_00014_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/btn_radio_anim_00015_qntm_alpha.png b/core/res/res/drawable-xhdpi/btn_radio_anim_00015_qntm_alpha.png
new file mode 100644
index 0000000..567bb0c
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/btn_radio_anim_00015_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/scrubber_control_off_qntm_alpha.png b/core/res/res/drawable-xhdpi/scrubber_control_off_qntm_alpha.png
index ad72f06..729e0bf 100644
--- a/core/res/res/drawable-xhdpi/scrubber_control_off_qntm_alpha.png
+++ b/core/res/res/drawable-xhdpi/scrubber_control_off_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/scrubber_control_on_qntm_alpha.png b/core/res/res/drawable-xhdpi/scrubber_control_on_qntm_alpha.png
index 7aceed1..d018a7c 100644
--- a/core/res/res/drawable-xhdpi/scrubber_control_on_qntm_alpha.png
+++ b/core/res/res/drawable-xhdpi/scrubber_control_on_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/switch_off_qntm_alpha.9.png b/core/res/res/drawable-xhdpi/switch_off_qntm_alpha.9.png
index 309b528..a7a972c 100644
--- a/core/res/res/drawable-xhdpi/switch_off_qntm_alpha.9.png
+++ b/core/res/res/drawable-xhdpi/switch_off_qntm_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/switch_on_qntm_alpha.9.png b/core/res/res/drawable-xhdpi/switch_on_qntm_alpha.9.png
index 139795e..dd8910b 100644
--- a/core/res/res/drawable-xhdpi/switch_on_qntm_alpha.9.png
+++ b/core/res/res/drawable-xhdpi/switch_on_qntm_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_anim_00000_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_check_anim_00000_qntm_alpha.png
new file mode 100644
index 0000000..1881f54
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_check_anim_00000_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_anim_00001_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_check_anim_00001_qntm_alpha.png
new file mode 100644
index 0000000..6f8ec2d
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_check_anim_00001_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_anim_00002_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_check_anim_00002_qntm_alpha.png
new file mode 100644
index 0000000..c954ed9
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_check_anim_00002_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_anim_00003_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_check_anim_00003_qntm_alpha.png
new file mode 100644
index 0000000..9d1a47e
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_check_anim_00003_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_anim_00004_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_check_anim_00004_qntm_alpha.png
new file mode 100644
index 0000000..ce63631
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_check_anim_00004_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_anim_00005_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_check_anim_00005_qntm_alpha.png
new file mode 100644
index 0000000..430c134
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_check_anim_00005_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_anim_00006_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_check_anim_00006_qntm_alpha.png
new file mode 100644
index 0000000..cdebf83
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_check_anim_00006_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_anim_00007_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_check_anim_00007_qntm_alpha.png
new file mode 100644
index 0000000..40ceadb
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_check_anim_00007_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_anim_00008_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_check_anim_00008_qntm_alpha.png
new file mode 100644
index 0000000..fb13eb2
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_check_anim_00008_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_anim_00009_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_check_anim_00009_qntm_alpha.png
new file mode 100644
index 0000000..d716fba
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_check_anim_00009_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_anim_00010_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_check_anim_00010_qntm_alpha.png
new file mode 100644
index 0000000..b8be041
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_check_anim_00010_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_anim_00011_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_check_anim_00011_qntm_alpha.png
new file mode 100644
index 0000000..bad0c3c
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_check_anim_00011_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_anim_00012_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_check_anim_00012_qntm_alpha.png
new file mode 100644
index 0000000..a6368fb
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_check_anim_00012_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_anim_00013_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_check_anim_00013_qntm_alpha.png
new file mode 100644
index 0000000..234e5d1
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_check_anim_00013_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_anim_00014_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_check_anim_00014_qntm_alpha.png
new file mode 100644
index 0000000..3e7796d
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_check_anim_00014_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_check_anim_00015_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_check_anim_00015_qntm_alpha.png
new file mode 100644
index 0000000..0673999
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_check_anim_00015_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_anim_00000_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_radio_anim_00000_qntm_alpha.png
new file mode 100644
index 0000000..4779944
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_radio_anim_00000_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_anim_00001_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_radio_anim_00001_qntm_alpha.png
new file mode 100644
index 0000000..866f7b7
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_radio_anim_00001_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_anim_00002_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_radio_anim_00002_qntm_alpha.png
new file mode 100644
index 0000000..76aae57
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_radio_anim_00002_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_anim_00003_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_radio_anim_00003_qntm_alpha.png
new file mode 100644
index 0000000..0cd470a1
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_radio_anim_00003_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_anim_00004_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_radio_anim_00004_qntm_alpha.png
new file mode 100644
index 0000000..0015b39
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_radio_anim_00004_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_anim_00005_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_radio_anim_00005_qntm_alpha.png
new file mode 100644
index 0000000..2f69f5b
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_radio_anim_00005_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_anim_00006_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_radio_anim_00006_qntm_alpha.png
new file mode 100644
index 0000000..77142fd
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_radio_anim_00006_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_anim_00007_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_radio_anim_00007_qntm_alpha.png
new file mode 100644
index 0000000..2f81277
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_radio_anim_00007_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_anim_00008_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_radio_anim_00008_qntm_alpha.png
new file mode 100644
index 0000000..d37fe60
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_radio_anim_00008_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_anim_00009_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_radio_anim_00009_qntm_alpha.png
new file mode 100644
index 0000000..cb62079
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_radio_anim_00009_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_anim_00010_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_radio_anim_00010_qntm_alpha.png
new file mode 100644
index 0000000..82dc428
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_radio_anim_00010_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_anim_00011_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_radio_anim_00011_qntm_alpha.png
new file mode 100644
index 0000000..2cba2fb
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_radio_anim_00011_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_anim_00012_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_radio_anim_00012_qntm_alpha.png
new file mode 100644
index 0000000..5de1952
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_radio_anim_00012_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_anim_00013_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_radio_anim_00013_qntm_alpha.png
new file mode 100644
index 0000000..1c22a17
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_radio_anim_00013_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_anim_00014_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_radio_anim_00014_qntm_alpha.png
new file mode 100644
index 0000000..7f652fc
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_radio_anim_00014_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/btn_radio_anim_00015_qntm_alpha.png b/core/res/res/drawable-xxhdpi/btn_radio_anim_00015_qntm_alpha.png
new file mode 100644
index 0000000..076acbd
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/btn_radio_anim_00015_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/scrubber_control_off_qntm_alpha.png b/core/res/res/drawable-xxhdpi/scrubber_control_off_qntm_alpha.png
index c11b0ae..a2b5716 100644
--- a/core/res/res/drawable-xxhdpi/scrubber_control_off_qntm_alpha.png
+++ b/core/res/res/drawable-xxhdpi/scrubber_control_off_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/scrubber_control_on_qntm_alpha.png b/core/res/res/drawable-xxhdpi/scrubber_control_on_qntm_alpha.png
index cde797e..caabc2c 100644
--- a/core/res/res/drawable-xxhdpi/scrubber_control_on_qntm_alpha.png
+++ b/core/res/res/drawable-xxhdpi/scrubber_control_on_qntm_alpha.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/switch_off_qntm_alpha.9.png b/core/res/res/drawable-xxhdpi/switch_off_qntm_alpha.9.png
index 9e234af..8d79a13 100644
--- a/core/res/res/drawable-xxhdpi/switch_off_qntm_alpha.9.png
+++ b/core/res/res/drawable-xxhdpi/switch_off_qntm_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/switch_on_qntm_alpha.9.png b/core/res/res/drawable-xxhdpi/switch_on_qntm_alpha.9.png
index b371eab..e0e4ef9 100644
--- a/core/res/res/drawable-xxhdpi/switch_on_qntm_alpha.9.png
+++ b/core/res/res/drawable-xxhdpi/switch_on_qntm_alpha.9.png
Binary files differ
diff --git a/core/res/res/drawable/btn_check_quantum_anim.xml b/core/res/res/drawable/btn_check_quantum_anim.xml
index 4b329ad..96715a4 100644
--- a/core/res/res/drawable/btn_check_quantum_anim.xml
+++ b/core/res/res/drawable/btn_check_quantum_anim.xml
@@ -1,3 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
 <!-- Copyright (C) 2014 The Android Open Source Project
 
      Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,21 +14,92 @@
      limitations under the License.
 -->
 
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:versionCode="1" >
+<animated-selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_enabled="false" android:state_checked="true">
+        <bitmap android:src="@drawable/btn_check_anim_00015_qntm_alpha"
+            android:tint="?attr/colorControlActivated"
+            android:alpha="?attr/disabledAlpha" />
+    </item>
+    <item android:state_enabled="false">
+        <bitmap android:src="@drawable/btn_check_anim_00000_qntm_alpha"
+            android:tint="?attr/colorControlNormal"
+            android:alpha="?attr/disabledAlpha" />
+    </item>
+    <item android:state_checked="true" android:id="@+id/on">
+        <bitmap android:src="@drawable/btn_check_anim_00015_qntm_alpha"
+            android:tint="?attr/colorControlActivated" />
+    </item>
+    <item android:id="@+id/off">
+        <bitmap android:src="@drawable/btn_check_anim_00000_qntm_alpha"
+            android:tint="?attr/colorControlNormal" />
+    </item>
+    <transition android:fromId="@+id/off" android:toId="@+id/on" android:reversible="true">
+        <animation-list>
+            <item android:duration="33">
+                <bitmap android:src="@drawable/btn_check_anim_00000_qntm_alpha"
+                    android:tint="?attr/colorControlActivated" />
+            </item>
+            <item android:duration="33">
+                <bitmap android:src="@drawable/btn_check_anim_00001_qntm_alpha"
+                    android:tint="?attr/colorControlActivated" />
+            </item>
+            <item android:duration="33">
+                <bitmap android:src="@drawable/btn_check_anim_00002_qntm_alpha"
+                    android:tint="?attr/colorControlActivated" />
+            </item>
+            <item android:duration="33">
+                <bitmap android:src="@drawable/btn_check_anim_00003_qntm_alpha"
+                    android:tint="?attr/colorControlActivated" />
+            </item>
+            <item android:duration="33">
+                <bitmap android:src="@drawable/btn_check_anim_00004_qntm_alpha"
+                    android:tint="?attr/colorControlActivated" />
+            </item>
+            <item android:duration="33">
+                <bitmap android:src="@drawable/btn_check_anim_00005_qntm_alpha"
+                    android:tint="?attr/colorControlActivated" />
+            </item>
+            <item android:duration="33">
+                <bitmap android:src="@drawable/btn_check_anim_00006_qntm_alpha"
+                    android:tint="?attr/colorControlActivated" />
+            </item>
+            <item android:duration="33">
+                <bitmap android:src="@drawable/btn_check_anim_00007_qntm_alpha"
+                    android:tint="?attr/colorControlActivated" />
+            </item>
+            <item android:duration="33">
+                <bitmap android:src="@drawable/btn_check_anim_00008_qntm_alpha"
+                    android:tint="?attr/colorControlActivated" />
+            </item>
+            <item android:duration="33">
+                <bitmap android:src="@drawable/btn_check_anim_00009_qntm_alpha"
+                    android:tint="?attr/colorControlActivated" />
+            </item>
+            <item android:duration="33">
+                <bitmap android:src="@drawable/btn_check_anim_00010_qntm_alpha"
+                    android:tint="?attr/colorControlActivated" />
+            </item>
+            <item android:duration="33">
+                <bitmap android:src="@drawable/btn_check_anim_00011_qntm_alpha"
+                    android:tint="?attr/colorControlActivated" />
+            </item>
+            <item android:duration="33">
+                <bitmap android:src="@drawable/btn_check_anim_00012_qntm_alpha"
+                    android:tint="?attr/colorControlActivated" />
+            </item>
+            <item android:duration="33">
+                <bitmap android:src="@drawable/btn_check_anim_00013_qntm_alpha"
+                    android:tint="?attr/colorControlActivated" />
+            </item>
+            <item android:duration="33">
+                <bitmap android:src="@drawable/btn_check_anim_00014_qntm_alpha"
+                    android:tint="?attr/colorControlActivated" />
+            </item>
+            <item android:duration="33">
+                <bitmap android:src="@drawable/btn_check_anim_00015_qntm_alpha"
+                    android:tint="?attr/colorControlActivated" />
+            </item>
+        </animation-list>
+    </transition>
+</animated-selector>
 
-    <size
-        android:height="32dp"
-        android:width="32dp" />
-
-    <viewport
-        android:viewportHeight="320"
-        android:viewportWidth="320" />
-
-    <group>
-        <path
-            android:name="check"
-            android:pathData="M 232.1,80.6 L 248.5,92.1 L 145.2,239.5 L 71.5,187.8 L 83,171.5 L 140.3,211.7 z"
-            android:fill="?attr/colorControlActivated" />
-    </group>
-</vector>
diff --git a/core/res/res/drawable/btn_radio_quantum_anim.xml b/core/res/res/drawable/btn_radio_quantum_anim.xml
new file mode 100644
index 0000000..5068b7a
--- /dev/null
+++ b/core/res/res/drawable/btn_radio_quantum_anim.xml
@@ -0,0 +1,104 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<animated-selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_enabled="false" android:state_checked="true">
+        <bitmap android:src="@drawable/btn_radio_anim_00015_qntm_alpha"
+            android:tint="?attr/colorControlActivated"
+            android:alpha="?attr/disabledAlpha" />
+    </item>
+    <item android:state_enabled="false">
+        <bitmap android:src="@drawable/btn_radio_anim_00000_qntm_alpha"
+            android:tint="?attr/colorControlNormal"
+            android:alpha="?attr/disabledAlpha" />
+    </item>
+    <item android:state_checked="true" android:id="@+id/on">
+        <bitmap android:src="@drawable/btn_radio_anim_00015_qntm_alpha"
+            android:tint="?attr/colorControlActivated" />
+    </item>
+    <item android:id="@+id/off">
+        <bitmap android:src="@drawable/btn_radio_anim_00000_qntm_alpha"
+            android:tint="?attr/colorControlNormal" />
+    </item>
+    <transition android:fromId="@+id/off" android:toId="@+id/on" android:reversible="true">
+        <animation-list>
+            <item android:duration="33">
+                <bitmap android:src="@drawable/btn_radio_anim_00000_qntm_alpha"
+                    android:tint="?attr/colorControlActivated" />
+            </item>
+            <item android:duration="33">
+                <bitmap android:src="@drawable/btn_radio_anim_00001_qntm_alpha"
+                    android:tint="?attr/colorControlActivated" />
+            </item>
+            <item android:duration="33">
+                <bitmap android:src="@drawable/btn_radio_anim_00002_qntm_alpha"
+                    android:tint="?attr/colorControlActivated" />
+            </item>
+            <item android:duration="33">
+                <bitmap android:src="@drawable/btn_radio_anim_00003_qntm_alpha"
+                    android:tint="?attr/colorControlActivated" />
+            </item>
+            <item android:duration="33">
+                <bitmap android:src="@drawable/btn_radio_anim_00004_qntm_alpha"
+                    android:tint="?attr/colorControlActivated" />
+            </item>
+            <item android:duration="33">
+                <bitmap android:src="@drawable/btn_radio_anim_00005_qntm_alpha"
+                    android:tint="?attr/colorControlActivated" />
+            </item>
+            <item android:duration="33">
+                <bitmap android:src="@drawable/btn_radio_anim_00006_qntm_alpha"
+                    android:tint="?attr/colorControlActivated" />
+            </item>
+            <item android:duration="33">
+                <bitmap android:src="@drawable/btn_radio_anim_00007_qntm_alpha"
+                    android:tint="?attr/colorControlActivated" />
+            </item>
+            <item android:duration="33">
+                <bitmap android:src="@drawable/btn_radio_anim_00008_qntm_alpha"
+                    android:tint="?attr/colorControlActivated" />
+            </item>
+            <item android:duration="33">
+                <bitmap android:src="@drawable/btn_radio_anim_00009_qntm_alpha"
+                    android:tint="?attr/colorControlActivated" />
+            </item>
+            <item android:duration="33">
+                <bitmap android:src="@drawable/btn_radio_anim_00010_qntm_alpha"
+                    android:tint="?attr/colorControlActivated" />
+            </item>
+            <item android:duration="33">
+                <bitmap android:src="@drawable/btn_radio_anim_00011_qntm_alpha"
+                    android:tint="?attr/colorControlActivated" />
+            </item>
+            <item android:duration="33">
+                <bitmap android:src="@drawable/btn_radio_anim_00012_qntm_alpha"
+                    android:tint="?attr/colorControlActivated" />
+            </item>
+            <item android:duration="33">
+                <bitmap android:src="@drawable/btn_radio_anim_00013_qntm_alpha"
+                    android:tint="?attr/colorControlActivated" />
+            </item>
+            <item android:duration="33">
+                <bitmap android:src="@drawable/btn_radio_anim_00014_qntm_alpha"
+                    android:tint="?attr/colorControlActivated" />
+            </item>
+            <item android:duration="33">
+                <bitmap android:src="@drawable/btn_radio_anim_00015_qntm_alpha"
+                    android:tint="?attr/colorControlActivated" />
+            </item>
+        </animation-list>
+    </transition>
+</animated-selector>
diff --git a/core/res/res/drawable/scrubber_progress_horizontal_quantum.xml b/core/res/res/drawable/scrubber_progress_horizontal_quantum.xml
index d172b05..f82fe7a 100644
--- a/core/res/res/drawable/scrubber_progress_horizontal_quantum.xml
+++ b/core/res/res/drawable/scrubber_progress_horizontal_quantum.xml
@@ -16,22 +16,26 @@
 
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
     <item android:state_enabled="false">
-        <bitmap android:src="@drawable/scrubber_track_qntm_alpha"
+        <nine-patch android:src="@drawable/scrubber_track_qntm_alpha"
             android:tint="?attr/colorControlNormal" />
     </item>
     <item>
         <layer-list>
             <item android:id="@id/background">
-                <bitmap android:src="@drawable/scrubber_track_qntm_alpha"
+                <nine-patch android:src="@drawable/scrubber_track_qntm_alpha"
                     android:tint="?attr/colorControlNormal" />
             </item>
             <item android:id="@id/secondaryProgress">
-                <bitmap android:src="@drawable/scrubber_primary_qntm_alpha"
-                    android:tint="?attr/colorControlNormal" />
+                <scale android:scaleWidth="100%">
+                    <nine-patch android:src="@drawable/scrubber_primary_qntm_alpha"
+                        android:tint="?attr/colorControlNormal" />
+                </scale>
             </item>
             <item android:id="@id/progress">
-                <bitmap android:src="@drawable/scrubber_primary_qntm_alpha"
-                    android:tint="?attr/colorControlActivated" />
+                <scale android:scaleWidth="100%">
+                    <nine-patch android:src="@drawable/scrubber_primary_qntm_alpha"
+                        android:tint="?attr/colorControlActivated" />
+                </scale>
             </item>
         </layer-list>
     </item>
diff --git a/core/res/res/layout/alert_dialog_micro.xml b/core/res/res/layout/alert_dialog_micro.xml
index f8eb46c..abdbd16 100644
--- a/core/res/res/layout/alert_dialog_micro.xml
+++ b/core/res/res/layout/alert_dialog_micro.xml
@@ -20,6 +20,8 @@
     android:id="@+id/parentPanel"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
+    android:background="@android:color/white"
+    android:layout_gravity="center"
     android:orientation="vertical">
 
     <LinearLayout android:id="@+id/topPanel"
diff --git a/core/res/res/values-km-rKH/strings.xml b/core/res/res/values-km-rKH/strings.xml
index 4d6f0a3..bee6c21 100644
--- a/core/res/res/values-km-rKH/strings.xml
+++ b/core/res/res/values-km-rKH/strings.xml
@@ -68,7 +68,7 @@
   </plurals>
     <string name="imei" msgid="2625429890869005782">"IMEI"</string>
     <string name="meid" msgid="4841221237681254195">"MEID"</string>
-    <string name="ClipMmi" msgid="6952821216480289285">"លេខ​សម្គាល់​អ្នក​ហៅ​​ចូល"</string>
+    <string name="ClipMmi" msgid="6952821216480289285">"លេខ​សម្គាល់​អ្នក​ហៅ​​ចូល​"</string>
     <string name="ClirMmi" msgid="7784673673446833091">"លេខ​សម្គាល់​អ្នក​ហៅ​ចេញ"</string>
     <string name="CfMmi" msgid="5123218989141573515">"បញ្ជូន​ការ​ហៅ​បន្ត"</string>
     <string name="CwMmi" msgid="9129678056795016867">"រង់ចាំ​ការ​ហៅ"</string>
@@ -125,7 +125,7 @@
     <string name="cfTemplateRegisteredTime" msgid="6781621964320635172">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g> ៖ មិន​បាន​បញ្ជូន​បន្ត"</string>
     <string name="fcComplete" msgid="3118848230966886575">"កូដ​លក្ខណៈ​ពេញលេញ។"</string>
     <string name="fcError" msgid="3327560126588500777">"បញ្ហា​ការ​តភ្ជាប់​ ឬ​កូដ​លក្ខណៈ​​​មិន​ត្រឹមត្រូវ​។"</string>
-    <string name="httpErrorOk" msgid="1191919378083472204">"យល់​ព្រម"</string>
+    <string name="httpErrorOk" msgid="1191919378083472204">"យល់​ព្រម​"</string>
     <string name="httpError" msgid="7956392511146698522">"មាន​កំហុស​បណ្ដាញ។"</string>
     <string name="httpErrorLookup" msgid="4711687456111963163">"រក​មិន​ឃើញ URL ។"</string>
     <string name="httpErrorUnsupportedAuthScheme" msgid="6299980280442076799">"គ្រោងការណ៍​ផ្ទៀងផ្ទាត់​តំបន់បណ្ដាញ​មិន​ត្រូវ​បាន​គាំទ្រ។"</string>
@@ -183,7 +183,7 @@
     <string name="global_action_silent_mode_off_status" msgid="1506046579177066419">"បើក​សំឡេង"</string>
     <string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"ពេល​ជិះ​យន្តហោះ"</string>
     <string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"បាន​បើក​របៀប​ពេល​ជិះ​យន្ត​ហោះ"</string>
-    <string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"បាន​បិទ​របៀបពេលជិះ​យន្តហោះ"</string>
+    <string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"បាន​បិទ​របៀបពេលជិះ​យន្តហោះ​"</string>
     <string name="global_action_settings" msgid="1756531602592545966">"ការ​កំណត់"</string>
     <string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
     <string name="safeMode" msgid="2788228061547930246">"របៀប​​​សុវត្ថិភាព"</string>
@@ -195,7 +195,7 @@
     <string name="permgrouplab_messages" msgid="7521249148445456662">"សារ​របស់​អ្នក"</string>
     <string name="permgroupdesc_messages" msgid="7821999071003699236">"អាន និង​សរសេរ​សារ SMS, អ៊ីមែល និង​សារ​ផ្សេងៗ​ទៀត​របស់​អ្នក។"</string>
     <string name="permgrouplab_personalInfo" msgid="3519163141070533474">"ព័ត៌មាន​ផ្ទាល់ខ្លួន​របស់​អ្នក"</string>
-    <string name="permgroupdesc_personalInfo" msgid="8426453129788861338">"ចូល​ដំណើរការ​ព័ត៌មាន​ដោយ​ផ្ទាល់​អំពី​អ្នក​ ដែល​បា​ន​រក្សាទុក​ក្នុង​កាត​ទំនាក់ទំនង​របស់​អ្នក។"</string>
+    <string name="permgroupdesc_personalInfo" msgid="8426453129788861338">"ចូល​ដំណើរការ​ព័ត៌មាន​ដោយ​ផ្ទាល់​អំពី​អ្នក​ ដែល​បា​ន​រក្សាទុក​ក្នុង​កាត​ទំនាក់ទំនង​របស់​អ្នក។​"</string>
     <string name="permgrouplab_socialInfo" msgid="5799096623412043791">"ព័ត៌មាន​សង្គម​របស់​អ្នក"</string>
     <string name="permgroupdesc_socialInfo" msgid="7129842457611643493">"ចូល​ដំណើរការ​ព័ត៌មាន​ដោយ​ផ្ទាល់​អំពី​ទំនាក់ទំនង និង​ការ​ភ្ជាប់​សង្គម​របស់​អ្នក។"</string>
     <string name="permgrouplab_location" msgid="635149742436692049">"ទីតាំង​របស់​អ្នក"</string>
@@ -386,7 +386,7 @@
     <string name="permdesc_readInputState" msgid="8387754901688728043">"ឲ្យ​កម្មវិធី​មើល​គ្រាប់​ចុច​ដែល​អ្នក​ចុច​ពេល​មាន​អន្តរកម្ម​ជា​មួយ​កម្មវិធី​ផ្សេង (ដូចជា បញ្ចូល​ពាក្យ​សម្ងាត់)។ មិន​គួរ​ចាំបាច់​សម្រាប់​កម្មវិធី​ធម្មតា​ទេ។"</string>
     <string name="permlab_bindInputMethod" msgid="3360064620230515776">"ចង​ទៅ​វិធីសាស្ត្រ​បញ្ចូល"</string>
     <string name="permdesc_bindInputMethod" msgid="3250440322807286331">"ឲ្យ​ម្ចាស់​ចង​ចំណុច​ប្រទាក់​កម្រិត​កំពូល​នៃ​វិធី​សាស្ត្រ​បញ្ចូល។ មិន​គួរ​​ចាំបាច់​សម្រាប់​កម្មវិធី​ធម្មតា​ទេ។"</string>
-    <string name="permlab_bindAccessibilityService" msgid="5357733942556031593">"ចង​សេវា​កម្ម​ភាព​មធ្យោបាយ​ងាយស្រួល"</string>
+    <string name="permlab_bindAccessibilityService" msgid="5357733942556031593">"ចង​សេវា​កម្ម​ភាព​មធ្យោបាយ​ងាយស្រួល​"</string>
     <string name="permdesc_bindAccessibilityService" msgid="7034615928609331368">"ឲ្យ​​ម្ចាស់​ចង​ចំណុច​ប្រទាក់​កម្រិត​កំពូល​នៃ​សេវាកម្ម​ភាព​ងាយស្រួល។ មិន​គួរ​ចាំបាច់​សម្រាប់​កម្មវិធី​ធម្មតា​ទេ។"</string>
     <string name="permlab_bindPrintService" msgid="8462815179572748761">"ចង​សេវាកម្ម​​បោះពុម្ព"</string>
     <string name="permdesc_bindPrintService" msgid="7960067623209111135">"ឲ្យ​ម្ចាស់​ចង​ចំណុច​ប្រទាក់​កម្រិត​កំពូល​នៃ​សេវាកម្ម​ធាតុ​ក្រាហ្វិក។ មិន​គួរ​​ចាំបាច់​សម្រាប់​កម្មវិធី​ធម្មតា​ទេ។"</string>
@@ -404,7 +404,7 @@
     <string name="permdesc_bindVoiceInteraction" msgid="2345721766501778101">"អនុញ្ញាត​ឲ្យ​ម្ចាស់​ភ្ជាប់​ទៅ​ចំណុច​ប្រទាក់​កម្រិត​កំពូល​​របស់​សេវាកម្ម​អន្តរកម្ម​សំឡេង។ មិន​គួរ​ចាំបាច់​សម្រាប់​កម្មវិធី​ធម្មតា​ទេ។"</string>
     <string name="permlab_bindRemoteDisplay" msgid="1782923938029941960">"ភ្ជាប់​ទៅ​ការ​បង្ហាញ​ពី​ចម្ងាយ"</string>
     <string name="permdesc_bindRemoteDisplay" msgid="1261242718727295981">"អនុញ្ញាត​ឲ្យ​ម្ចាស់​ភ្ជាប់​​ទៅ​ចំណុច​ប្រទាក់​កម្រិត​កំពូល​នៃ​ការ​បង្ហាញ​ពី​ចម្ងាយ។ មិន​គួរ​ចាំបាច់​សម្រាប់​កម្មវិធី​ធម្មតា​ទេ។"</string>
-    <string name="permlab_bindRemoteViews" msgid="5697987759897367099">"ចង​សេវា​កម្ម​ធាតុ​ក្រាហ្វិក"</string>
+    <string name="permlab_bindRemoteViews" msgid="5697987759897367099">"ចង​សេវា​កម្ម​ធាតុ​ក្រាហ្វិក​"</string>
     <string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"ឲ្យ​ម្ចាស់​ចង​ចំណុច​ប្រទាក់​កម្រិត​កំពូល​នៃ​សេវាកម្ម​ធាតុ​ក្រាហ្វិក។ មិន​គួរ​​ចាំបាច់​សម្រាប់​កម្មវិធី​ធម្មតា​ទេ។"</string>
     <string name="permlab_bindRouteProvider" msgid="4869394607915096847">"ភ្ជាប់​ទៅ​សេវាកម្ម​ក្រុមហ៊ុន​ផ្ដល់​ច្រក"</string>
     <string name="permdesc_bindRouteProvider" msgid="4703804520859960329">"អនុញ្ញាត​ឲ្យ​ម្ចាស់​ភ្ជាប់​ទៅ​ក្រុមហ៊ុន​ផ្ដល់​​ច្រក​ដែល​បាន​ចុះ​ឈ្មោះ។ មិន​គួរ​ចាំបាច់​សម្រាប់​កម្មវិធី​ធម្មតា​ទេ។"</string>
@@ -412,7 +412,7 @@
     <string name="permdesc_bindDeviceAdmin" msgid="569715419543907930">"ឲ្យ​ម្ចាស់​ផ្ញើ​គោលបំណង​​ទៅ​អ្នក​គ្រប់គ្រង​ឧបករណ៍។ មិន​គួរ​ចាំបាច់​សម្រាប់​កម្មវិធី​ធម្មតា​ទេ។"</string>
     <string name="permlab_bindTvInput" msgid="5601264742478168987">"ភ្ជាប់​ទៅ​ការ​បញ្ចូល​ទូរទស្សន៍"</string>
     <string name="permdesc_bindTvInput" msgid="2371008331852001924">"អនុញ្ញាត​ឲ្យ​ម្ចាស់​ភ្ជាប់​ទៅ​ចំណុចប្រទាក់​កម្រិត​ខ្ពស់​នៃ​ការ​បញ្ចូល​ទូរទស្សន៍។ មិន​គួរ​ចាំបាច់​សម្រាប់​កម្មវិធី​ធម្មតា​ទេ។"</string>
-    <string name="permlab_manageDeviceAdmins" msgid="4248828900045808722">"បន្ថែម​ ឬ​លុប​កម្មវិធី​គ្រប់គ្រង​​​ឧបករណ៍"</string>
+    <string name="permlab_manageDeviceAdmins" msgid="4248828900045808722">"បន្ថែម​ ឬ​លុប​កម្មវិធី​គ្រប់គ្រង​​​ឧបករណ៍​​"</string>
     <string name="permdesc_manageDeviceAdmins" msgid="5025608167709942485">"អនុញ្ញាត​​​ឲ្យ​ម្ចាស់​​​បន្ថែម​ ឬ​លុប​កម្មវិធី​គ្រប់គ្រង​ឧបករណ៍​សកម្ម​ចេញ​។ មិន​គួរ​ប្រើ​សម្រាប់​កម្មវិធី​​ធម្មតា​ទេ​។"</string>
     <string name="permlab_setOrientation" msgid="3365947717163866844">"ប្ដូរ​ទិស​អេក្រង់"</string>
     <string name="permdesc_setOrientation" msgid="3046126619316671476">"ឲ្យ​កម្មវិធី​ប្ដូរ​ការ​បង្វិល​អេក្រង់​នៅ​ពេល​ណា​មួយ។ មិន​ចាំបាច់​សម្រាប់​កម្មវិធី​ធម្មតា​ទេ។"</string>
@@ -424,9 +424,9 @@
     <string name="permdesc_signalPersistentProcesses" msgid="4896992079182649141">"ឲ្យ​កម្មវិធី​ស្នើ​​សញ្ញា​ដែល​បាន​ផ្ដល់​ត្រូវ​ផ្ញើ​ទៅ​ដំណើរការ​ស្ថិតស្ថេរ​​ទាំង​អស់។"</string>
     <string name="permlab_persistentActivity" msgid="8841113627955563938">"ធ្វើ​ឲ្យ​កម្មវិធី​ដំណើរការ​ជា​និច្ច"</string>
     <string name="permdesc_persistentActivity" product="tablet" msgid="8525189272329086137">"ឲ្យ​កម្មវិធី​ធ្វើជា​ផ្នែក​​ស្ថិតស្ថេរ​ដោយ​ខ្លួន​ឯង​ក្នុង​អង្គ​ចងចាំ។ វា​អាច​កំណត់​អង្គ​ចងចាំ​ដែល​អាច​ប្រើ​បាន​ចំពោះ​កម្មវិធី​ផ្សេងៗ​ ដោយ​ធ្វើឲ្យ​កុំព្យូទ័រ​បន្ទះ​យឺត។"</string>
-    <string name="permdesc_persistentActivity" product="default" msgid="4384760047508278272">"ឲ្យ​កម្មវិធី ធ្វើជា​ផ្នែក​អចិន្ត្រៃយ៍​នៃ​ខ្លួន​ក្នុង​អង្គ​ចងចាំ។ វា​អាច​កម្រិត​អង្គ​ចងចាំ​អាច​ប្រើ​បាន​ ដើម្បី​ធ្វើ​ឲ្យ​កម្មវិធី​ផ្សេង​ធ្វើ​ឲ្យ​ទូរស័ព្ទ​របស់​អ្នក​យឺត។"</string>
+    <string name="permdesc_persistentActivity" product="default" msgid="4384760047508278272">"ឲ្យ​កម្មវិធី ធ្វើជា​ផ្នែក​អចិន្ត្រៃយ៍​នៃ​ខ្លួន​ក្នុង​អង្គ​ចងចាំ។ វា​អាច​កម្រិត​អង្គ​ចងចាំ​អាច​ប្រើ​បាន​ ដើម្បី​ធ្វើ​ឲ្យ​កម្មវិធី​ផ្សេង​ធ្វើ​ឲ្យ​ទូរស័ព្ទ​របស់​អ្នក​យឺត។​"</string>
     <string name="permlab_deletePackages" msgid="184385129537705938">"លុប​កម្មវិធី"</string>
-    <string name="permdesc_deletePackages" msgid="7411480275167205081">"ឲ្យ​កម្មវិធី​លុប​កញ្ចប់ Android ។ កម្មវិធី​ព្យាបាទ​អាច​ប្រើ​វា ដើម្បី​លុប​កម្មវិធី​សំខាន់​ៗ។"</string>
+    <string name="permdesc_deletePackages" msgid="7411480275167205081">"ឲ្យ​កម្មវិធី​លុប​កញ្ចប់ Android ។ កម្មវិធី​ព្យាបាទ​អាច​ប្រើ​វា ដើម្បី​លុប​កម្មវិធី​សំខាន់​ៗ។ ​"</string>
     <string name="permlab_clearAppUserData" msgid="274109191845842756">"លុប​ទិន្នន័យ​របស់​​កម្មវិធី​ផ្សេង"</string>
     <string name="permdesc_clearAppUserData" msgid="4625323684125459488">"ឲ្យ​កម្មវិធី​សម្អាត​ទិន្នន័យ​អ្នក​ប្រើ។"</string>
     <string name="permlab_deleteCacheFiles" msgid="3128665571837408675">"លុប​ឃ្លាំង​សម្ងាត់​កម្មវិធី​ផ្សេងៗ"</string>
@@ -477,7 +477,7 @@
     <string name="permdesc_writeContacts" product="tablet" msgid="897243932521953602">"ឲ្យ​កម្មវិធី​កែ​ទិន្នន័យ​អំពី​ទំនាក់ទំនង​របស់​អ្នក​ដែល​បាន​រក្សាទុក​ក្នុង​កុំព្យូទ័រ​បន្ទះ រួមមាន​ប្រេកង់​​ដែល​អ្នក​បាន​ហៅ អ៊ីមែល ឬ​ទាក់ទង​តាម​វិធី​ផ្សេងៗ​ជា​មួយ​ទំនាក់ទំនង​ជាក់លាក់។ សិទ្ធិ​​នេះ​អនុញ្ញាត​ឲ្យ​​​កម្មវិធី​លុប​ទិន្នន័យ​ទំនាក់ទំនង​របស់​អ្នក។"</string>
     <string name="permdesc_writeContacts" product="default" msgid="589869224625163558">"ឲ្យ​កម្មវិធី​កែ​ទិន្នន័យ​អំពី​ទំនាក់ទំនង​របស់​អ្នក​ដែល​បាន​រក្សាទុក​ក្នុង​ទូរស័ព្ទ​របស់​អ្នក រួមមាន​ប្រេកង់​ដែល​អ្នក​បាន​ហៅ អ៊ីមែល ឬ​បាន​ទាក់ទង​​តាម​វិធី​ផ្សេងៗ​ជា​មួយ​ទំនាក់​ទំនាក់​ជាក់លាក់។ សិទ្ធិ​នេះ​ឲ្យ​កម្មវិធី​លុប​ទិន្នន័យ​ទំនាក់ទំនង។"</string>
     <string name="permlab_readCallLog" msgid="3478133184624102739">"អាន​​កំណត់​ហេតុ​​​ហៅ"</string>
-    <string name="permdesc_readCallLog" product="tablet" msgid="3700645184870760285">"ឲ្យ​កម្មវិធី​អាន​បញ្ជី​ហៅ​កុំព្យូទ័រ​បន្ទះ​របស់​អ្នក រួមមាន​ទិន្នន័យ​អំពី​ការ​ហៅ​ចូល និង​ចេញ។ សិទ្ធិ​នេះ​អនុញ្ញាត​ឲ្យ​កម្មវិធី​រក្សាទុក​ទិន្នន័យ​បញ្ជី​ហៅ​របស់​អ្នក ហើយ​កម្មវិធី​ព្យាបាទ​អាច​ចែករំលែក​ទិន្នន័យ​បញ្ជី​ហៅ​ដោយ​មិន​ឲ្យ​អ្នក​ដឹង។"</string>
+    <string name="permdesc_readCallLog" product="tablet" msgid="3700645184870760285">"ឲ្យ​កម្មវិធី​អាន​បញ្ជី​ហៅ​កុំព្យូទ័រ​បន្ទះ​របស់​អ្នក រួមមាន​ទិន្នន័យ​អំពី​ការ​ហៅ​ចូល និង​ចេញ។ សិទ្ធិ​នេះ​អនុញ្ញាត​ឲ្យ​កម្មវិធី​រក្សាទុក​ទិន្នន័យ​បញ្ជី​ហៅ​របស់​អ្នក ហើយ​កម្មវិធី​ព្យាបាទ​អាច​ចែករំលែក​ទិន្នន័យ​បញ្ជី​ហៅ​ដោយ​មិន​ឲ្យ​អ្នក​ដឹង។​"</string>
     <string name="permdesc_readCallLog" product="default" msgid="5777725796813217244">"ឲ្យ​កម្មវិធី​អាន​​​បញ្ជី​ហៅ​ទូរស័ព្ទ​របស់​អ្នក រួមមាន​ទិន្នន័យ​អំពី​ការ​ហៅ​ចូល និង​ចេញ។ សិទ្ធិ​នេះ​អនុញ្ញាត​ឲ្យ​កម្មវិធី​រក្សាទុក​ទិន្នន័យ​បញ្ជី​ហៅ​របស់​អ្នក ហើយ​កម្មវិធី​ព្យាបាទ​អាច​ចែករំលែក​ទិន្នន័យ​បញ្ជី​ហៅ​ដោយ​មិន​ឲ្យ​អ្នកដឹង។"</string>
     <string name="permlab_writeCallLog" msgid="8552045664743499354">"សរសេរ​បញ្ជី​ហៅ"</string>
     <string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"ឲ្យ​កម្មវិធី​កែ​បញ្ជី​ហៅ​កុំព្យូទ័រ​បន្ទះ​របស់​អ្នក​រួមមាន​ទិន្នន័យ​អំពី​ការ​ហៅ​ចូល និង​ចេញ។​កម្មវិធី​ព្យាបាទ​អាច​ប្រើ​វា ដើម្បី​លុប ឬ​កែ​បញ្ជី​ហៅ​របស់​អ្នក។"</string>
@@ -611,7 +611,7 @@
     <string name="permdesc_setWallpaperHints" msgid="8235784384223730091">"ឲ្យ​កម្មវិធី​កំណត់​ជំនួយ​ទំហំ​ផ្ទាំង​រូបភាព​ប្រព័ន្ធ។"</string>
     <string name="permlab_masterClear" msgid="2315750423139697397">"កំណត់​ប្រព័ន្ធ​ទៅ​លំនាំដើម​រោងចក្រ​ឡើងវិញ"</string>
     <string name="permdesc_masterClear" msgid="3665380492633910226">"ឲ្យ​កម្មវិធី​កំណត់​ប្រព័ន្ធ​​ដូច​ការ​កំណត់​ចេញ​ពី​រោងចក្រ​ឡើងវិញ​ពេញលេញ ដោយ​លុប​ទិន្នន័យ ការ​កំណត់​រចនាសម្ព័ន្ធ និង​កម្មវិធី​បាន​ដំឡើង។"</string>
-    <string name="permlab_setTime" msgid="2021614829591775646">"កំណត់​​ម៉ោង"</string>
+    <string name="permlab_setTime" msgid="2021614829591775646">"កំណត់​​ម៉ោង​"</string>
     <string name="permdesc_setTime" product="tablet" msgid="1896341438151152881">"ឲ្យ​កម្មវិធី​ប្ដូរ​ម៉ោង​កុំព្យូទ័រ​បន្ទះ។"</string>
     <string name="permdesc_setTime" product="default" msgid="1855702730738020">"ឲ្យ​កម្មវិធី​ប្ដូរ​ម៉ោង​ទូរស័ព្ទ។"</string>
     <string name="permlab_setTimeZone" msgid="2945079801013077340">"កំណត់​តំបន់​ពេលវេលា"</string>
@@ -777,7 +777,7 @@
   <string-array name="organizationTypes">
     <item msgid="7546335612189115615">"កន្លែង​ធ្វើការ"</item>
     <item msgid="4378074129049520373">"ផ្សេងៗ"</item>
-    <item msgid="3455047468583965104">"តាម​តម្រូវ​ការ"</item>
+    <item msgid="3455047468583965104">"តាម​តម្រូវ​ការ​"</item>
   </string-array>
   <string-array name="imProtocols">
     <item msgid="8595261363518459565">"AIM"</item>
@@ -793,7 +793,7 @@
     <string name="phoneTypeHome" msgid="2570923463033985887">"ផ្ទះ"</string>
     <string name="phoneTypeMobile" msgid="6501463557754751037">"​ចល័ត"</string>
     <string name="phoneTypeWork" msgid="8863939667059911633">"កន្លែង​ធ្វើការ"</string>
-    <string name="phoneTypeFaxWork" msgid="3517792160008890912">"ទូរសារ​កន្លែង​ធ្វើការ"</string>
+    <string name="phoneTypeFaxWork" msgid="3517792160008890912">"ទូរសារ​កន្លែង​ធ្វើការ​"</string>
     <string name="phoneTypeFaxHome" msgid="2067265972322971467">"ទូរសារ​ផ្ទះ"</string>
     <string name="phoneTypePager" msgid="7582359955394921732">"ភេយ័រ"</string>
     <string name="phoneTypeOther" msgid="1544425847868765990">"ផ្សេងៗ"</string>
@@ -918,7 +918,7 @@
     <string name="lockscreen_glogin_too_many_attempts" msgid="2751368605287288808">"ព្យាយាម​លំនាំ​ច្រើន​ពេក"</string>
     <string name="lockscreen_glogin_instructions" msgid="3931816256100707784">"ដើម្បី​ដោះ​សោ ចូល​គណនី Google របស់​អ្នក។"</string>
     <string name="lockscreen_glogin_username_hint" msgid="8846881424106484447">"ឈ្មោះ​អ្នក​ប្រើ (អ៊ីមែល​)"</string>
-    <string name="lockscreen_glogin_password_hint" msgid="5958028383954738528">"ពាក្យសម្ងាត់"</string>
+    <string name="lockscreen_glogin_password_hint" msgid="5958028383954738528">"ពាក្យសម្ងាត់​"</string>
     <string name="lockscreen_glogin_submit_button" msgid="7130893694795786300">"ចូល"</string>
     <string name="lockscreen_glogin_invalid_input" msgid="1364051473347485908">"ឈ្មោះ​អ្នកប្រើ ឬ​ពាក្យ​សម្ងាត់​មិន​ត្រឹមត្រូវ។"</string>
     <string name="lockscreen_glogin_account_recovery_hint" msgid="1696924763690379073">"ភ្លេច​ឈ្មោះ​អ្នក​ប្រើ ឬ​ពាក្យ​សម្ងាត់​របស់​អ្នក?\nមើល "<b>"google.com/accounts/recovery"</b>" ។"</string>
@@ -963,7 +963,7 @@
     <string name="factorytest_failed" msgid="5410270329114212041">"បាន​បរាជ័យ​ក្នុង​ការ​សាកល្បង​រោងចក្រ"</string>
     <string name="factorytest_not_system" msgid="4435201656767276723">"សកម្មភាព FACTORY_TEST ត្រូវ​បាន​គាំទ្រ​សម្រាប់​តែ​កញ្ចប់​បាន​ដំឡើង​ក្នុង /system/app."</string>
     <string name="factorytest_no_action" msgid="872991874799998561">"រក​មិន​ឃើញ​កញ្ចប់​ដែល​ផ្ដល់​សកម្មភាព FACTORY_TEST ។"</string>
-    <string name="factorytest_reboot" msgid="6320168203050791643">"ចាប់​ផ្ដើម​ឡើង​វិញ"</string>
+    <string name="factorytest_reboot" msgid="6320168203050791643">"ចាប់​ផ្ដើម​ឡើង​វិញ​"</string>
     <string name="js_dialog_title" msgid="1987483977834603872">"ទំព័រ​មាន​ចំណងជើង \"<xliff:g id="TITLE">%s</xliff:g>\" សរសេរ៖"</string>
     <string name="js_dialog_title_default" msgid="6961903213729667573">"JavaScript"</string>
     <string name="js_dialog_before_unload_title" msgid="2619376555525116593">"បញ្ជាក់​ការ​រុករក"</string>
@@ -1021,7 +1021,7 @@
     <string name="prepend_shortcut_label" msgid="2572214461676015642">"ម៉ឺនុយ +"</string>
     <string name="menu_space_shortcut_label" msgid="2410328639272162537">"ដកឃ្លា"</string>
     <string name="menu_enter_shortcut_label" msgid="2743362785111309668">"enter"</string>
-    <string name="menu_delete_shortcut_label" msgid="3658178007202748164">"លុប"</string>
+    <string name="menu_delete_shortcut_label" msgid="3658178007202748164">"លុប​"</string>
     <string name="search_go" msgid="8298016669822141719">"ស្វែងរក"</string>
     <string name="searchview_description_search" msgid="6749826639098512120">"ស្វែងរក"</string>
     <string name="searchview_description_query" msgid="5911778593125355124">"ស្វែងរក​សំណួរ"</string>
@@ -1105,18 +1105,18 @@
     <string name="preposition_for_date" msgid="9093949757757445117">"នៅ <xliff:g id="DATE">%s</xliff:g>"</string>
     <string name="preposition_for_time" msgid="5506831244263083793">"នៅ​ម៉ោង <xliff:g id="TIME">%s</xliff:g>"</string>
     <string name="preposition_for_year" msgid="5040395640711867177">"ក្នុង​ឆ្នាំ <xliff:g id="YEAR">%s</xliff:g>"</string>
-    <string name="day" msgid="8144195776058119424">"ថ្ងៃ"</string>
+    <string name="day" msgid="8144195776058119424">"ថ្ងៃ​"</string>
     <string name="days" msgid="4774547661021344602">"​ថ្ងៃ"</string>
     <string name="hour" msgid="2126771916426189481">"ម៉ោង"</string>
     <string name="hours" msgid="894424005266852993">"ម៉ោង"</string>
-    <string name="minute" msgid="9148878657703769868">"នាទី"</string>
+    <string name="minute" msgid="9148878657703769868">"នាទី​"</string>
     <string name="minutes" msgid="5646001005827034509">"នាទី"</string>
-    <string name="second" msgid="3184235808021478">"វិនាទី"</string>
+    <string name="second" msgid="3184235808021478">"វិនាទី​"</string>
     <string name="seconds" msgid="3161515347216589235">"វិនាទី"</string>
-    <string name="week" msgid="5617961537173061583">"សប្ដាហ៍"</string>
-    <string name="weeks" msgid="6509623834583944518">"សប្ដាហ៍"</string>
-    <string name="year" msgid="4001118221013892076">"ឆ្នាំ"</string>
-    <string name="years" msgid="6881577717993213522">"ឆ្នាំ"</string>
+    <string name="week" msgid="5617961537173061583">"សប្ដាហ៍​"</string>
+    <string name="weeks" msgid="6509623834583944518">"សប្ដាហ៍​"</string>
+    <string name="year" msgid="4001118221013892076">"ឆ្នាំ​"</string>
+    <string name="years" msgid="6881577717993213522">"ឆ្នាំ​"</string>
   <plurals name="duration_seconds">
     <item quantity="one" msgid="6962015528372969481">"1 វិនាទី"</item>
     <item quantity="other" msgid="1886107766577166786">"<xliff:g id="COUNT">%d</xliff:g> វិនាទី"</item>
@@ -1132,12 +1132,12 @@
     <string name="VideoView_error_title" msgid="3534509135438353077">"បញ្ហា​វីដេអូ"</string>
     <string name="VideoView_error_text_invalid_progressive_playback" msgid="3186670335938670444">"វីដេអូ​នេះ​មិន​ត្រឹមត្រូវ​សម្រាប់​​ចរន្ត​ចូល​ឧបករណ៍​នេះ។"</string>
     <string name="VideoView_error_text_unknown" msgid="3450439155187810085">"មិន​អាច​ចាក់​វីដេអូ​នេះ។"</string>
-    <string name="VideoView_error_button" msgid="2822238215100679592">"យល់​ព្រម"</string>
+    <string name="VideoView_error_button" msgid="2822238215100679592">"យល់​ព្រម​"</string>
     <string name="relative_time" msgid="1818557177829411417">"<xliff:g id="DATE">%1$s</xliff:g>, <xliff:g id="TIME">%2$s</xliff:g>"</string>
     <string name="noon" msgid="7245353528818587908">"រសៀល"</string>
     <string name="Noon" msgid="3342127745230013127">"រសៀល"</string>
     <string name="midnight" msgid="7166259508850457595">"កណ្ដាលអធ្រាត្រ"</string>
-    <string name="Midnight" msgid="5630806906897892201">"កណ្ដាល​អធ្រាត្រ"</string>
+    <string name="Midnight" msgid="5630806906897892201">"កណ្ដាល​អធ្រាត្រ​"</string>
     <string name="elapsed_time_short_format_mm_ss" msgid="4431555943828711473">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
     <string name="elapsed_time_short_format_h_mm_ss" msgid="1846071997616654124">"<xliff:g id="HOURS">%1$d</xliff:g>:<xliff:g id="MINUTES">%2$02d</xliff:g>:<xliff:g id="SECONDS">%3$02d</xliff:g>"</string>
     <string name="selectAll" msgid="6876518925844129331">"ជ្រើស​ទាំងអស់"</string>
@@ -1154,13 +1154,13 @@
     <string name="inputMethod" msgid="1653630062304567879">"វិធីសាស្ត្រ​បញ្ចូល"</string>
     <string name="editTextMenuTitle" msgid="4909135564941815494">"សកម្មភាព​អត្ថបទ"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"អស់​ទំហំ​ផ្ទុក"</string>
-    <string name="low_internal_storage_view_text" msgid="6640505817617414371">"មុខងារ​ប្រព័ន្ធ​មួយ​ចំនួន​អាច​មិន​ដំណើរការ"</string>
+    <string name="low_internal_storage_view_text" msgid="6640505817617414371">"មុខងារ​ប្រព័ន្ធ​មួយ​ចំនួន​អាច​មិន​ដំណើរការ​"</string>
     <string name="app_running_notification_title" msgid="8718335121060787914">"<xliff:g id="APP_NAME">%1$s</xliff:g> កំពុង​ដំណើរការ"</string>
     <string name="app_running_notification_text" msgid="4653586947747330058">"ប៉ះ​ ដើម្បី​មើល​ព័ត៌មាន​បន្ថែម ឬ​បញ្ឈប់​កម្មវិធី។"</string>
-    <string name="ok" msgid="5970060430562524910">"យល់​ព្រម"</string>
-    <string name="cancel" msgid="6442560571259935130">"បោះ​បង់"</string>
-    <string name="yes" msgid="5362982303337969312">"យល់​ព្រម"</string>
-    <string name="no" msgid="5141531044935541497">"បោះ​បង់"</string>
+    <string name="ok" msgid="5970060430562524910">"យល់​ព្រម​"</string>
+    <string name="cancel" msgid="6442560571259935130">"បោះ​បង់​"</string>
+    <string name="yes" msgid="5362982303337969312">"យល់​ព្រម​"</string>
+    <string name="no" msgid="5141531044935541497">"បោះ​បង់​"</string>
     <string name="dialog_alert_title" msgid="2049658708609043103">"ប្រយ័ត្ន"</string>
     <string name="loading" msgid="7933681260296021180">"កំពុង​ផ្ទុក..."</string>
     <string name="capital_on" msgid="1544682755514494298">"បើក"</string>
@@ -1169,7 +1169,7 @@
     <string name="whichHomeApplication" msgid="4616420172727326782">"ជ្រើស​កម្មវិធី​ដើម"</string>
     <string name="alwaysUse" msgid="4583018368000610438">"ប្រើ​តាម​លំនាំដើម​សម្រាប់​សកម្មភាព​នេះ។"</string>
     <string name="clearDefaultHintMsg" msgid="3252584689512077257">"សម្អាត​លំនាំដើម​ក្នុង​ការកំណត់​ប្រព័ន្ធ &gt; កម្មវិធី &gt; ទាញ​យក។"</string>
-    <string name="chooseActivity" msgid="7486876147751803333">"ជ្រើស​សកម្មភាព"</string>
+    <string name="chooseActivity" msgid="7486876147751803333">"ជ្រើស​សកម្មភាព​​"</string>
     <string name="chooseUsbActivity" msgid="6894748416073583509">"ជ្រើស​កម្មវិធី​សម្រាប់​ឧបករណ៍​យូអេសប៊ី"</string>
     <string name="noApplications" msgid="2991814273936504689">"គ្មាន​កម្មវិធី​អាច​អនុវត្ត​សកម្មភាព​នេះ។"</string>
     <string name="aerr_title" msgid="1905800560317137752"></string>
@@ -1180,7 +1180,7 @@
     <string name="anr_activity_process" msgid="5776209883299089767">"សកម្មភាព <xliff:g id="ACTIVITY">%1$s</xliff:g> មិន​ឆ្លើយតប។\n\nតើ​អ្នក​ចង់​បិទ​វា?"</string>
     <string name="anr_application_process" msgid="8941757607340481057">"<xliff:g id="APPLICATION">%1$s</xliff:g> មិន​ឆ្លើយតប។ តើ​អ្នក​ចង់​បិទ​វា?"</string>
     <string name="anr_process" msgid="6513209874880517125">"ដំណើរការ <xliff:g id="PROCESS">%1$s</xliff:g> មិន​ឆ្លើយតប។ \n\nតើ​អ្នក​ចង់​បិទ​វា​ឬ?"</string>
-    <string name="force_close" msgid="8346072094521265605">"យល់​ព្រម"</string>
+    <string name="force_close" msgid="8346072094521265605">"យល់​ព្រម​"</string>
     <string name="report" msgid="4060218260984795706">"រាយការណ៍"</string>
     <string name="wait" msgid="7147118217226317732">"រង់ចាំ"</string>
     <string name="webpage_unresponsive" msgid="3272758351138122503">"ទំព័រ​ក្លាយ​ជា​មិន​ឆ្លើយតប។\n\nតើ​អ្នក​​ចង់​បិទ​វា?"</string>
@@ -1262,19 +1262,19 @@
     <string name="sms_short_code_details" msgid="3492025719868078457"><font fgcolor="#ffffb060">"នេះ​អាច​កាត់​លុយ"</font>" លើ​គណនី​ចល័ត​របស់​អ្នក។"</string>
     <string name="sms_premium_short_code_details" msgid="5523826349105123687"><font fgcolor="#ffffb060">"វា​នឹង​គិត​ថ្លៃ​សេវាកម្ម​លើ​គណនី​ចល័ត​របស់​អ្នក។"</font></string>
     <string name="sms_short_code_confirm_allow" msgid="4458878637111023413">"ផ្ញើ"</string>
-    <string name="sms_short_code_confirm_deny" msgid="2927389840209170706">"បោះ​បង់"</string>
+    <string name="sms_short_code_confirm_deny" msgid="2927389840209170706">"បោះ​បង់​"</string>
     <string name="sms_short_code_remember_choice" msgid="5289538592272218136">"ចងចាំ​ជម្រើស​របស់​ខ្ញុំ"</string>
     <string name="sms_short_code_remember_undo_instruction" msgid="4960944133052287484">"អ្នក​អាច​ប្ដូរ​វា​ពេល​ក្រោយ​ក្នុង​ការ​កំណត់ &gt; កម្មវិធី"</string>
     <string name="sms_short_code_confirm_always_allow" msgid="3241181154869493368">"អនុញ្ញាត​ជា​និច្ច"</string>
     <string name="sms_short_code_confirm_never_allow" msgid="446992765774269673">"កុំ​អនុញ្ញាត"</string>
     <string name="sim_removed_title" msgid="6227712319223226185">"បាន​ដក​ស៊ីម​កាត​ចេញ"</string>
-    <string name="sim_removed_message" msgid="2333164559970958645">"បណ្ដាញ​ចល័ត​នឹង​ប្រើ​លែង​បាន​រហូត​ដល់​អ្នក​ចាប់ផ្ដើម​ជា​មួយ​ស៊ីម​កាត​ដែល​បា​បញ្ចូល​ត្រឹមត្រូវ។"</string>
+    <string name="sim_removed_message" msgid="2333164559970958645">"បណ្ដាញ​ចល័ត​នឹង​ប្រើ​លែង​បាន​រហូត​ដល់​អ្នក​ចាប់ផ្ដើម​ជា​មួយ​ស៊ីម​កាត​ដែល​បា​បញ្ចូល​ត្រឹមត្រូវ។​"</string>
     <string name="sim_done_button" msgid="827949989369963775">"រួចរាល់"</string>
     <string name="sim_added_title" msgid="3719670512889674693">"បាន​បន្ថែម​ស៊ីម​កាត"</string>
     <string name="sim_added_message" msgid="6599945301141050216">"ចាប់ផ្ដើម​ឧបករណ៍​របស់​អ្នក​ឡើង​វិញ ដើម្បី​ចូល​ដំណើរការ​បណ្ដាញ​ចល័ត។"</string>
     <string name="sim_restart_button" msgid="4722407842815232347">"ចាប់ផ្ដើម​ឡើងវិញ"</string>
-    <string name="time_picker_dialog_title" msgid="8349362623068819295">"កំណត់​ម៉ោង"</string>
-    <string name="date_picker_dialog_title" msgid="5879450659453782278">"កំណត់​កាល​បរិច្ឆេទ"</string>
+    <string name="time_picker_dialog_title" msgid="8349362623068819295">"កំណត់​ម៉ោង​"</string>
+    <string name="date_picker_dialog_title" msgid="5879450659453782278">"កំណត់​កាល​បរិច្ឆេទ​"</string>
     <string name="date_time_set" msgid="5777075614321087758">"កំណត់"</string>
     <string name="date_time_done" msgid="2507683751759308828">"រួចរាល់"</string>
     <string name="perms_new_perm_prefix" msgid="8257740710754301407"><font size="12" fgcolor="#ff33b5e5">"ថ្មី៖ "</font></string>
@@ -1352,7 +1352,7 @@
     <string name="permdesc_copyProtectedData" msgid="4390697124288317831">"ឲ្យ​កម្មវិធី​ដក​សេវាកម្ម​នៃ​កម្មវិធី​ផ្ទុក​​លំនាំដើម ដើម្បី​ចម្លង​មាតិកា។​ មិន​សម្រាប់​ប្រើ​ដោយ​កម្មវិធី​លំនាំដើម។"</string>
     <string name="permlab_route_media_output" msgid="1642024455750414694">"នាំ​ផ្លូវ​លទ្ធផល​មេឌៀ"</string>
     <string name="permdesc_route_media_output" msgid="4932818749547244346">"ឲ្យ​កម្មវិធី​នាំ​ផ្លូវ​លទ្ធផល​មេឌៀ​ទៅ​ឧបករណ៍​​ខាង​ក្រៅ​ផ្សេង។"</string>
-    <string name="permlab_access_keyguard_secure_storage" msgid="7565552237977815047">"ចូល​ដំណើរការ​ឧបករណ៍​ផ្ទុក​សុវត្ថិភាព"</string>
+    <string name="permlab_access_keyguard_secure_storage" msgid="7565552237977815047">"ចូល​ដំណើរការ​ឧបករណ៍​ផ្ទុក​សុវត្ថិភាព​"</string>
     <string name="permdesc_access_keyguard_secure_storage" msgid="5866245484303285762">"ឲ្យ​កម្មវិធី​ចូល​​ការ​ផ្ទុក​មាន​សុវត្ថិភាព keguard ។"</string>
     <string name="permlab_control_keyguard" msgid="172195184207828387">"ពិនិត្យ​ការ​បង្ហាញ និង​លាក់​ការ​ការពារ"</string>
     <string name="permdesc_control_keyguard" msgid="3043732290518629061">"ឲ្យ​កម្មវិធី​គ្រប់គ្រង keguard ។"</string>
@@ -1367,7 +1367,7 @@
     <string name="ime_action_go" msgid="8320845651737369027">"ទៅ"</string>
     <string name="ime_action_search" msgid="658110271822807811">"ស្វែងរក"</string>
     <string name="ime_action_send" msgid="2316166556349314424">"ផ្ញើ"</string>
-    <string name="ime_action_next" msgid="3138843904009813834">"បន្ទាប់"</string>
+    <string name="ime_action_next" msgid="3138843904009813834">"បន្ទាប់​"</string>
     <string name="ime_action_done" msgid="8971516117910934605">"រួចរាល់"</string>
     <string name="ime_action_previous" msgid="1443550039250105948">"មុន"</string>
     <string name="ime_action_default" msgid="2840921885558045721">"អនុវត្ត"</string>
@@ -1376,7 +1376,7 @@
     <string name="grant_credentials_permission_message_header" msgid="2106103817937859662">"កម្មវិធី​មួយ ឬ​ច្រើន​ដូច​ខាង​ក្រោម​ស្នើ​សិទ្ធិ ដើម្បី​ចូល​គណនី​របស់​អ្នក​ឥឡូវ និង​ពេល​អនាគត។"</string>
     <string name="grant_credentials_permission_message_footer" msgid="3125211343379376561">"តើ​អ្នក​ចង់​អនុញ្ញាត​សំណើ​នេះ?"</string>
     <string name="grant_permissions_header_text" msgid="6874497408201826708">"ស្នើ​ចូល"</string>
-    <string name="allow" msgid="7225948811296386551">"អនុញ្ញាត"</string>
+    <string name="allow" msgid="7225948811296386551">"អនុញ្ញាត​"</string>
     <string name="deny" msgid="2081879885755434506">"បដិសេធ"</string>
     <string name="permission_request_notification_title" msgid="6486759795926237907">"បាន​ស្នើ​សិទ្ធិ"</string>
     <string name="permission_request_notification_with_subtitle" msgid="8530393139639560189">"បាន​ស្នើ​សិទ្ធិ\nសម្រាប់​គណនី <xliff:g id="ACCOUNT">%s</xliff:g> ។"</string>
@@ -1399,12 +1399,12 @@
     <string name="no_file_chosen" msgid="6363648562170759465">"គ្មាន​ឯកសារ​បាន​ជ្រើស"</string>
     <string name="reset" msgid="2448168080964209908">"កំណត់​ឡើងវិញ"</string>
     <string name="submit" msgid="1602335572089911941">"ដាក់​ស្នើ"</string>
-    <string name="car_mode_disable_notification_title" msgid="3164768212003864316">"បាន​បើក​របៀប​រថយន្ត"</string>
+    <string name="car_mode_disable_notification_title" msgid="3164768212003864316">"បាន​បើក​របៀប​រថយន្ត​"</string>
     <string name="car_mode_disable_notification_message" msgid="8035230537563503262">"ប៉ះ​ ដើម្បី​ចេញ​ពី​របៀប​រថយន្ត​។"</string>
     <string name="tethered_notification_title" msgid="3146694234398202601">"ភ្ជាប់ ឬ​ហតស្ពត​សកម្ម"</string>
     <string name="tethered_notification_message" msgid="6857031760103062982">"ប៉ះ​ ដើម្បី​រៀបចំ។"</string>
     <string name="back_button_label" msgid="2300470004503343439">"ថយក្រោយ"</string>
-    <string name="next_button_label" msgid="1080555104677992408">"បន្ទាប់"</string>
+    <string name="next_button_label" msgid="1080555104677992408">"បន្ទាប់​"</string>
     <string name="skip_button_label" msgid="1275362299471631819">"រំលង"</string>
     <string name="throttle_warning_notification_title" msgid="4890894267454867276">"ការ​ប្រើ​ទិន្នន័យ​ចល័ត​ខ្ពស់"</string>
     <string name="throttle_warning_notification_message" msgid="3340822228599337743">"ប៉ះ​ ដើម្បី​​ស្វែងយល់​បន្ថែម​អំពី​ការ​ប្រើ​​​ទិន្នន័យ​ចល័ត​។"</string>
@@ -1430,7 +1430,7 @@
     <string name="media_shared" product="nosdcard" msgid="5830814349250834225">"ឧបករណ៍​ផ្ទុក​យូអេសប៊ី​បច្ចុប្បន្ន​កំពុង​ប្រើ​ដោយ​កុំព្យូទ័រ។"</string>
     <string name="media_shared" product="default" msgid="5706130568133540435">"បច្ចុប្បន្ន​កាត​អេសឌី​កំពុង​ប្រើ​ដោយ​កុំព្យូទ័រ"</string>
     <string name="media_unknown_state" msgid="729192782197290385">"មិន​ស្គាល់​ស្ថានភាព​មេឌៀ​ខាង​ក្រៅ។"</string>
-    <string name="share" msgid="1778686618230011964">"ចែក​រំលែក"</string>
+    <string name="share" msgid="1778686618230011964">"ចែក​រំលែក​"</string>
     <string name="find" msgid="4808270900322985960">"រក"</string>
     <string name="websearch" msgid="4337157977400211589">"ស្វែងរក​តាម​បណ្ដាញ"</string>
     <string name="find_next" msgid="5742124618942193978">"រក​បន្ទាប់"</string>
@@ -1446,7 +1446,7 @@
     <string name="sync_undo_deletes" msgid="2941317360600338602">"មិន​ធ្វើ​ការ​លុប​វិញ"</string>
     <string name="sync_do_nothing" msgid="3743764740430821845">"មិន​ធ្វើអ្វី​ទេ​ឥឡូវ"</string>
     <string name="choose_account_label" msgid="5655203089746423927">"ជ្រើស​គណនី"</string>
-    <string name="add_account_label" msgid="2935267344849993553">"បន្ថែម​គណនី​ថ្មី"</string>
+    <string name="add_account_label" msgid="2935267344849993553">"បន្ថែម​គណនី​ថ្មី​​"</string>
     <string name="add_account_button_label" msgid="3611982894853435874">"បន្ថែម​គណនី"</string>
     <string name="number_picker_increment_button" msgid="2412072272832284313">"បង្កើន"</string>
     <string name="number_picker_decrement_button" msgid="476050778386779067">"បន្ថយ"</string>
@@ -1465,15 +1465,15 @@
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"បង្កើន​​ឆ្នាំ"</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"បន្ថយ​ឆ្នាំ"</string>
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
-    <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"បោះ​បង់"</string>
+    <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"បោះ​បង់​"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"លុប"</string>
     <string name="keyboardview_keycode_done" msgid="1992571118466679775">"រួចរាល់"</string>
     <string name="keyboardview_keycode_mode_change" msgid="4547387741906537519">"ប្ដូរ​របៀប"</string>
     <string name="keyboardview_keycode_shift" msgid="2270748814315147690">"Shift"</string>
     <string name="keyboardview_keycode_enter" msgid="2985864015076059467">"Enter"</string>
-    <string name="activitychooserview_choose_application" msgid="2125168057199941199">"ជ្រើស​កម្មវិធី"</string>
+    <string name="activitychooserview_choose_application" msgid="2125168057199941199">"ជ្រើស​កម្មវិធី​​"</string>
     <string name="activitychooserview_choose_application_error" msgid="8624618365481126668">"មិន​អាច​ចាប់ផ្ដើម <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
-    <string name="shareactionprovider_share_with" msgid="806688056141131819">"ចែករំលែក​ជា​មួយ"</string>
+    <string name="shareactionprovider_share_with" msgid="806688056141131819">"ចែករំលែក​ជា​មួយ​"</string>
     <string name="shareactionprovider_share_with_application" msgid="5627411384638389738">"ចែក​រំលែក​ជា​មួយ <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
     <string name="content_description_sliding_handle" msgid="415975056159262248">"គ្រប់គ្រង​ការ​រុញ។ ប៉ះ &amp; សង្កត់។"</string>
     <string name="description_target_unlock_tablet" msgid="3833195335629795055">"អូស​ ដើម្បី​ដោះ​សោ។"</string>
@@ -1487,7 +1487,7 @@
     <string name="storage_internal" msgid="4891916833657929263">"ឧបករណ៍​ផ្ទុក​ខាង​ក្នុង"</string>
     <string name="storage_sd_card" msgid="3282948861378286745">"កាត​អេសឌី"</string>
     <string name="storage_usb" msgid="3017954059538517278">"ឧបករណ៍​ផ្ទុក​យូអេសប៊ី"</string>
-    <string name="extract_edit_menu_button" msgid="8940478730496610137">"កែសម្រួល"</string>
+    <string name="extract_edit_menu_button" msgid="8940478730496610137">"កែសម្រួល​"</string>
     <string name="data_usage_warning_title" msgid="1955638862122232342">"ការព្រមាន​ប្រើ​ទិន្នន័យ"</string>
     <string name="data_usage_warning_body" msgid="2814673551471969954">"ប៉ះ ដើម្បី​មើល​ការ​ប្រើ និង​ការ​កំណត់។"</string>
     <string name="data_usage_3g_limit_title" msgid="7093334419518706686">"បាន​បិទ​ទិន្នន័យ 2G​-3G"</string>
@@ -1544,7 +1544,7 @@
     <string name="media_route_status_available" msgid="6983258067194649391">"ទំនេរ"</string>
     <string name="media_route_status_not_available" msgid="6739899962681886401">"មិន​ទំនេរ"</string>
     <string name="media_route_status_in_use" msgid="4533786031090198063">"កំពុង​ប្រើ"</string>
-    <string name="display_manager_built_in_display_name" msgid="2583134294292563941">"អេក្រង់​ជាប់"</string>
+    <string name="display_manager_built_in_display_name" msgid="2583134294292563941">"អេក្រង់​ជាប់​"</string>
     <string name="display_manager_hdmi_display_name" msgid="1555264559227470109">"អេក្រង់ HDMI"</string>
     <string name="display_manager_overlay_display_name" msgid="5142365982271620716">"#<xliff:g id="ID">%1$d</xliff:g> ត្រួត​គ្នា"</string>
     <string name="display_manager_overlay_display_title" msgid="652124517672257172">"<xliff:g id="NAME">%1$s</xliff:g>: <xliff:g id="WIDTH">%2$d</xliff:g>x<xliff:g id="HEIGHT">%3$d</xliff:g>, <xliff:g id="DPI">%4$d</xliff:g> dpi"</string>
@@ -1576,7 +1576,7 @@
     <string name="kg_login_too_many_attempts" msgid="6486842094005698475">"ព្យាយាម​លំនាំ​ច្រើន​ពេក"</string>
     <string name="kg_login_instructions" msgid="1100551261265506448">"ដើម្បី​ដោះ​សោ ចូល​ក្នុង​គណនី Google ។"</string>
     <string name="kg_login_username_hint" msgid="5718534272070920364">"ឈ្មោះ​អ្នក​ប្រើ (អ៊ី​ម៉ែ​ល​)"</string>
-    <string name="kg_login_password_hint" msgid="9057289103827298549">"ពាក្យសម្ងាត់"</string>
+    <string name="kg_login_password_hint" msgid="9057289103827298549">"ពាក្យសម្ងាត់​"</string>
     <string name="kg_login_submit_button" msgid="5355904582674054702">"ចូល"</string>
     <string name="kg_login_invalid_input" msgid="5754664119319872197">"ឈ្មោះ​អ្នកប្រើ ឬ​ពាក្យ​សម្ងាត់​មិន​ត្រឹមត្រូវ។"</string>
     <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"ភ្លេច​ឈ្មោះ​អ្នកប្រើ ឬ​ពាក្យ​សម្ងាត់​របស់​អ្នក?\nមើល "<b>"google.com/accounts/recovery"</b>" ។"</string>
@@ -1685,7 +1685,7 @@
     <string name="mediasize_japanese_you4" msgid="2091777168747058008">"You4"</string>
     <string name="mediasize_unknown_portrait" msgid="3088043641616409762">"​មិន​ស្គាល់​បញ្ឈរ"</string>
     <string name="mediasize_unknown_landscape" msgid="4876995327029361552">"មិន​ស្គាល់​ទេសភាព"</string>
-    <string name="write_fail_reason_cancelled" msgid="7091258378121627624">"បាន​បោះ​បង់"</string>
+    <string name="write_fail_reason_cancelled" msgid="7091258378121627624">"បាន​បោះ​បង់​"</string>
     <string name="write_fail_reason_cannot_write" msgid="8132505417935337724">"កំហុស​ក្នុង​ការ​សរសេរ​មាតិកា"</string>
     <string name="reason_unknown" msgid="6048913880184628119">"មិន​ស្គាល់"</string>
     <string name="reason_service_unavailable" msgid="7824008732243903268">"មិន​បា​ន​បើក​សេវាកម្ម​បោះពុម្ព"</string>
diff --git a/core/res/res/values-lo-rLA/strings.xml b/core/res/res/values-lo-rLA/strings.xml
index 08ba7e1..7ec1376 100644
--- a/core/res/res/values-lo-rLA/strings.xml
+++ b/core/res/res/values-lo-rLA/strings.xml
@@ -1697,7 +1697,7 @@
     <string name="restr_pin_enter_old_pin" msgid="1462206225512910757">"PIN ປະ​ຈຸ​ບັນ"</string>
     <string name="restr_pin_enter_new_pin" msgid="5959606691619959184">"ລະຫັດ PIN ໃໝ່"</string>
     <string name="restr_pin_confirm_pin" msgid="8501523829633146239">"ຢືນຢັນລະຫັດ PIN ໃໝ່"</string>
-    <string name="restr_pin_create_pin" msgid="8017600000263450337">"ສ້າງ PIN ສໍາ​ລັບ​ການ​ປັບ​ປຸງ​ຂໍ້ຈໍາ​ກັດ"</string>
+    <string name="restr_pin_create_pin" msgid="8017600000263450337">"ສ້າງ PIN ສໍາ​ລັບ​ການ​ປັບ​ປຸງ​ຂໍ້ຈໍາ​ກັດ​"</string>
     <string name="restr_pin_error_doesnt_match" msgid="2224214190906994548">"PIN ບໍ່​ກົງກັນ. ລອງໃໝ່ອີກຄັ້ງ​."</string>
     <string name="restr_pin_error_too_short" msgid="8173982756265777792">"PIN ​ສັ້ນ​ເກີນ​ໄປ​. ຕ້ອງມີຢ່າງໜ້ອຍ 4 ຫຼັກ​."</string>
   <plurals name="restr_pin_countdown">
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index d9473ec..cedb92d 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -3343,6 +3343,8 @@
         <attr name="thumb" format="reference" />
         <!-- An offset for the thumb that allows it to extend out of the range of the track. -->
         <attr name="thumbOffset" format="dimension" />
+        <!-- Whether to split the track and leave a gap for the thumb drawable. -->
+        <attr name="splitTrack" format="boolean" />
     </declare-styleable>
 
     <declare-styleable name="StackView">
@@ -4239,6 +4241,58 @@
         <attr name="autoMirrored"/>
     </declare-styleable>
 
+    <!-- Drawable used to render several states with animated transitions. Each state
+         is represented by a child drawable with an optional keyframe ID. -->
+    <declare-styleable name="AnimatedStateListDrawable">
+        <!-- Indicates whether the drawable should be initially visible. -->
+        <attr name="visible" />
+        <!-- If true, allows the drawable's padding to change based on the
+             current state that is selected.  If false, the padding will
+             stay the same (based on the maximum padding of all the states).
+             Enabling this feature requires that the owner of the drawable
+             deal with performing layout when the state changes, which is
+             often not supported. -->
+        <attr name="variablePadding" />
+        <!-- If true, the drawable's reported internal size will remain
+             constant as the state changes; the size is the maximum of all
+             of the states.  If false, the size will vary based on the
+             current state. -->
+        <attr name="constantSize" />
+        <!-- Enables or disables dithering of the bitmap if the bitmap does not have the
+             same pixel configuration as the screen (for instance: a ARGB 8888 bitmap with
+             an RGB 565 screen). -->
+        <attr name="dither" />
+        <!-- Amount of time (in milliseconds) to fade in a new state drawable. -->
+        <attr name="enterFadeDuration" />
+        <!-- Amount of time (in milliseconds) to fade out an old state drawable. -->
+        <attr name="exitFadeDuration" />
+        <!-- Indicates if the drawable needs to be mirrored when its layout direction is
+             RTL (right-to-left). -->
+        <attr name="autoMirrored"/>
+    </declare-styleable>
+
+    <!-- Transition used to animate between states with keyframe IDs. -->
+    <declare-styleable name="AnimatedStateListDrawableItem">
+        <!-- Reference to a drawable resource to use for the frame.  If not
+             given, the drawable must be defined by the first child tag. -->
+        <attr name="drawable" />
+        <!-- Keyframe identifier for use in specifying transitions. -->
+        <attr name="id" />
+    </declare-styleable>
+
+    <!-- Transition used to animate between states with keyframe IDs. -->
+    <declare-styleable name="AnimatedStateListDrawableTransition">
+        <!-- Keyframe identifier for the starting state. -->
+        <attr name="fromId" format="reference" />
+        <!-- Keyframe identifier for the ending state. -->
+        <attr name="toId" format="reference" />
+        <!-- Reference to a animation drawable resource to use for the frame.  If not
+             given, the animation drawable must be defined by the first child tag. -->
+        <attr name="drawable" />
+        <!-- Whether this transition is reversible. -->
+        <attr name="reversible" format="boolean" />
+    </declare-styleable>
+
     <!-- Drawable used to render several animated frames. -->
     <declare-styleable name="AnimationDrawable">
         <attr name="visible" />
@@ -6341,6 +6395,8 @@
         <attr name="switchMinWidth" format="dimension" />
         <!-- Minimum space between the switch and caption text -->
         <attr name="switchPadding" format="dimension" />
+        <!-- Whether to split the track and leave a gap for the thumb drawable. -->
+        <attr name="splitTrack" />
     </declare-styleable>
 
     <declare-styleable name="Pointer">
@@ -6553,4 +6609,8 @@
         <attr name="layout_gravity" />
     </declare-styleable>
 
+    <!-- Used as a filter array on the theme to pull out only the EdgeEffect-relevant bits. -->
+    <declare-styleable name="EdgeEffect">
+        <attr name="colorPrimaryLight" />
+    </declare-styleable>
 </resources>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index f39155b..83cbb74 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1393,8 +1393,10 @@
         <item>com.android.inputmethod.latin</item>
     </string-array>
 
-    <string-array name="config_notificationScorers">
-        <item>com.android.internal.notification.PeopleNotificationScorer</item>
+    <!-- The list of classes that should be added to the notification ranking pipline.
+     See {@link com.android.server.notification.NotificationSignalExtractortor} -->
+    <string-array name="config_notificationSignalExtractors">
+        <item>com.android.server.notification.ValidateNotificationPeople</item>
     </string-array>
 
     <!-- Flag indicating that this device does not rotate and will always remain in its default
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 6b2c788..bf92f9b 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -143,6 +143,8 @@
     <!-- Preferred width of the search view. -->
     <dimen name="search_view_preferred_width">320dip</dimen>
 
+    <!-- Dialog padding for round display -->
+    <dimen name="alert_dialog_round_padding">27dip</dimen>
     <!-- Dialog title height -->
     <dimen name="alert_dialog_title_height">64dip</dimen>
     <!-- Dialog button bar height -->
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 4e584c0..dc5efea 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2166,6 +2166,10 @@
   <public type="attr" name="documentLaunchMode" />
   <public type="attr" name="autoRemoveFromRecents" />
   <public type="attr" name="stateListAnimator" />
+  <public type="attr" name="toId" />
+  <public type="attr" name="fromId" />
+  <public type="attr" name="reversible" />
+  <public type="attr" name="splitTrack" />
 
   <public-padding type="dimen" name="l_resource_pad" end="0x01050010" />
 
diff --git a/core/res/res/values/styles_micro.xml b/core/res/res/values/styles_micro.xml
index bdaa49d..5bac1f9 100644
--- a/core/res/res/values/styles_micro.xml
+++ b/core/res/res/values/styles_micro.xml
@@ -15,6 +15,16 @@
 -->
 <resources>
     <style name="AlertDialog.Micro" parent="AlertDialog.Holo.Light">
+        <item name="fullDark">@null</item>
+        <item name="topDark">@null</item>
+        <item name="centerDark">@null</item>
+        <item name="bottomDark">@null</item>
+        <item name="fullBright">@null</item>
+        <item name="topBright">@null</item>
+        <item name="centerBright">@null</item>
+        <item name="bottomBright">@null</item>
+        <item name="bottomMedium">@null</item>
+        <item name="centerMedium">@null</item>
         <item name="layout">@layout/alert_dialog_micro</item>
     </style>
 
diff --git a/core/res/res/values/styles_quantum.xml b/core/res/res/values/styles_quantum.xml
index 88a2a9f..e693673 100644
--- a/core/res/res/values/styles_quantum.xml
+++ b/core/res/res/values/styles_quantum.xml
@@ -455,10 +455,10 @@
     <style name="Widget.Quantum.CompoundButton.Switch">
         <item name="track">@drawable/switch_track_quantum</item>
         <item name="thumb">@drawable/switch_inner_quantum</item>
+        <item name="splitTrack">true</item>
         <item name="switchTextAppearance">@style/TextAppearance.Quantum.Widget.Switch</item>
         <item name="textOn"></item>
         <item name="textOff"></item>
-        <item name="thumbTextPadding">12dip</item>
         <item name="switchMinWidth">72dip</item>
         <item name="switchPadding">16dip</item>
         <item name="background">?attr/selectableItemBackground</item>
@@ -572,10 +572,8 @@
         <item name="indeterminateOnly">false</item>
         <item name="progressDrawable">@drawable/scrubber_progress_horizontal_quantum</item>
         <item name="indeterminateDrawable">@drawable/scrubber_progress_horizontal_quantum</item>
-        <item name="minHeight">13dip</item>
-        <item name="maxHeight">13dip</item>
         <item name="thumb">@drawable/scrubber_control_selector_quantum</item>
-        <item name="thumbOffset">16dip</item>
+        <item name="splitTrack">true</item>
         <item name="focusable">true</item>
         <item name="paddingStart">16dip</item>
         <item name="paddingEnd">16dip</item>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 2bf72e8..1057cc2 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -120,6 +120,7 @@
   <java-symbol type="id" name="overlay_display_window_title" />
   <java-symbol type="id" name="package_label" />
   <java-symbol type="id" name="packages_list" />
+  <java-symbol type="id" name="parentPanel" />
   <java-symbol type="id" name="pause" />
   <java-symbol type="id" name="perms_list" />
   <java-symbol type="id" name="perm_icon" />
@@ -325,6 +326,7 @@
   <java-symbol type="color" name="tab_indicator_text_v4" />
 
   <java-symbol type="dimen" name="accessibility_touch_slop" />
+  <java-symbol type="dimen" name="alert_dialog_round_padding"/>
   <java-symbol type="dimen" name="config_prefDialogWidth" />
   <java-symbol type="dimen" name="config_viewConfigurationTouchSlop" />
   <java-symbol type="dimen" name="config_viewMinFlingVelocity" />
@@ -1653,7 +1655,7 @@
   <java-symbol type="id" name="button_always" />
   <java-symbol type="integer" name="config_globalActionsKeyTimeout" />
   <java-symbol type="integer" name="config_maxResolverActivityColumns" />
-  <java-symbol type="array" name="config_notificationScorers" />
+  <java-symbol type="array" name="config_notificationSignalExtractors" />
 
   <java-symbol type="layout" name="notification_quantum_action" />
   <java-symbol type="layout" name="notification_quantum_action_list" />
diff --git a/core/res/res/values/themes_micro.xml b/core/res/res/values/themes_micro.xml
index 39df700..9647947 100644
--- a/core/res/res/values/themes_micro.xml
+++ b/core/res/res/values/themes_micro.xml
@@ -47,10 +47,15 @@
         <item name="textAppearanceInverse">@style/TextAppearance.Micro</item>
     </style>
 
-    <style name="Theme.Micro.Dialog.Alert" parent="Theme.Holo.Light.Dialog.Alert">
+    <style name="Theme.Micro.Dialog.Alert">
         <item name="windowTitleStyle">@style/DialogWindowTitle.Micro</item>
         <item name="alertDialogStyle">@style/AlertDialog.Micro</item>
         <item name="windowIsFloating">false</item>
+        <item name="windowBackground">@android:color/transparent</item>
+        <item name="windowOverscan">true</item>
+        <item name="windowContentOverlay">@null</item>
+        <item name="android:windowMinWidthMajor">@android:dimen/dialog_min_width_major</item>
+        <item name="android:windowMinWidthMinor">@android:dimen/dialog_min_width_minor</item>
     </style>
 
     <style name="Theme.Micro.Dialog.AppError" parent="Theme.Micro.Dialog">
diff --git a/core/res/res/values/themes_quantum.xml b/core/res/res/values/themes_quantum.xml
index 768fd9a..18d6f80 100644
--- a/core/res/res/values/themes_quantum.xml
+++ b/core/res/res/values/themes_quantum.xml
@@ -122,8 +122,8 @@
         <item name="listDivider">@drawable/list_divider_quantum</item>
         <item name="listSeparatorTextViewStyle">@style/Widget.Quantum.TextView.ListSeparator</item>
 
-        <item name="listChoiceIndicatorSingle">@drawable/btn_radio_quantum</item>
-        <item name="listChoiceIndicatorMultiple">@drawable/btn_check_quantum</item>
+        <item name="listChoiceIndicatorSingle">@drawable/btn_radio_quantum_anim</item>
+        <item name="listChoiceIndicatorMultiple">@drawable/btn_check_quantum_anim</item>
 
         <item name="listChoiceBackgroundIndicator">@drawable/list_selector_quantum</item>
 
@@ -466,8 +466,8 @@
         <item name="listDivider">@drawable/list_divider_quantum</item>
         <item name="listSeparatorTextViewStyle">@style/Widget.Quantum.Light.TextView.ListSeparator</item>
 
-        <item name="listChoiceIndicatorSingle">@drawable/btn_radio_quantum</item>
-        <item name="listChoiceIndicatorMultiple">@drawable/btn_check_quantum</item>
+        <item name="listChoiceIndicatorSingle">@drawable/btn_radio_quantum_anim</item>
+        <item name="listChoiceIndicatorMultiple">@drawable/btn_check_quantum_anim</item>
 
         <item name="listChoiceBackgroundIndicator">@drawable/list_selector_quantum</item>
 
diff --git a/core/tests/coretests/src/android/net/LinkSocketTest.java b/core/tests/coretests/src/android/net/LinkSocketTest.java
deleted file mode 100644
index af77d63..0000000
--- a/core/tests/coretests/src/android/net/LinkSocketTest.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright (C) 2010 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.net;
-
-import android.net.LinkSocket;
-import android.test.suitebuilder.annotation.SmallTest;
-import junit.framework.TestCase;
-
-/**
- * Test LinkSocket
- */
-public class LinkSocketTest extends TestCase {
-
-    @SmallTest
-    public void testBasic() throws Exception {
-        LinkSocket ls;
-
-        ls = new LinkSocket();
-        ls.close();
-    }
-
-    @SmallTest
-    public void testLinkCapabilities() throws Exception {
-        LinkCapabilities lc;
-
-        lc = new LinkCapabilities();
-        assertEquals(0, lc.size());
-        assertEquals(true, lc.isEmpty());
-    }
-}
diff --git a/docs/html/guide/topics/resources/string-resource.jd b/docs/html/guide/topics/resources/string-resource.jd
index 5a96ba1..e2326ec 100644
--- a/docs/html/guide/topics/resources/string-resource.jd
+++ b/docs/html/guide/topics/resources/string-resource.jd
@@ -20,9 +20,6 @@
 information about styling and formatting strings, see the section about <a
 href="#FormattingAndStyling">Formatting and Styling</a>.</p>
 
-
-
-
 <h2 id="String">String</h2>
 
 <p>A single string that can be referenced from the application or from other resource files (such
@@ -433,7 +430,7 @@
 
 
 
-<h3>Styling with HTML markup</h3>
+<h3 id="StylingWithHTML">Styling with HTML markup</h3>
 
 <p>You can add styling to your strings with HTML markup. For example:</p>
 <pre>
@@ -497,5 +494,107 @@
 CharSequence styledText = Html.fromHtml(text);
 </pre>
 
+<h2 id="StylingWithSpannables">Styling with Spannables</h2>
+<p>
+A {@link android.text.Spannable} is a text object that you can style with
+typeface properties such as color and font weight. You use
+{@link android.text.SpannableStringBuilder} to build
+your text and then apply styles defined in the {@link android.text.style}
+package to the text.
+</p>
 
+<p>You can use the following helper methods to set up much of the work
+of creating spannable text:</p>
 
+<pre style="pretty-print">
+/**
+ * Returns a CharSequence that concatenates the specified array of CharSequence
+ * objects and then applies a list of zero or more tags to the entire range.
+ *
+ * @param content an array of character sequences to apply a style to
+ * @param tags the styled span objects to apply to the content
+ *        such as android.text.style.StyleSpan
+ *
+ */
+private static CharSequence apply(CharSequence[] content, Object... tags) {
+    SpannableStringBuilder text = new SpannableStringBuilder();
+    openTags(text, tags);
+    for (CharSequence item : content) {
+        text.append(item);
+    }
+    closeTags(text, tags);
+    return text;
+}
+
+/**
+ * Iterates over an array of tags and applies them to the beginning of the specified
+ * Spannable object so that future text appended to the text will have the styling
+ * applied to it. Do not call this method directly.
+ */
+private static void openTags(Spannable text, Object[] tags) {
+    for (Object tag : tags) {
+        text.setSpan(tag, 0, 0, Spannable.SPAN_MARK_MARK);
+    }
+}
+
+/**
+ * "Closes" the specified tags on a Spannable by updating the spans to be
+ * endpoint-exclusive so that future text appended to the end will not take
+ * on the same styling. Do not call this method directly.
+ */
+private static void closeTags(Spannable text, Object[] tags) {
+    int len = text.length();
+    for (Object tag : tags) {
+        if (len > 0) {
+            text.setSpan(tag, 0, len, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+        } else {
+            text.removeSpan(tag);
+        }
+    }
+}
+</pre>
+
+<p>
+The following <code>bold</code>, <code>italic</code>, and <code>color</code>
+methods show you how to call the helper methods to apply
+styles defined in the {@link android.text.style} package. You
+can create similar methods to do other types of text styling.
+</p>
+
+<pre style="pretty-print">
+/**
+ * Returns a CharSequence that applies boldface to the concatenation
+ * of the specified CharSequence objects.
+ */
+public static CharSequence bold(CharSequence... content) {
+    return apply(content, new StyleSpan(Typeface.BOLD));
+}
+
+/**
+ * Returns a CharSequence that applies italics to the concatenation
+ * of the specified CharSequence objects.
+ */
+public static CharSequence italic(CharSequence... content) {
+    return apply(content, new StyleSpan(Typeface.ITALIC));
+}
+
+/**
+ * Returns a CharSequence that applies a foreground color to the
+ * concatenation of the specified CharSequence objects.
+ */
+public static CharSequence color(int color, CharSequence... content) {
+    return apply(content, new ForegroundColorSpan(color));
+}
+</pre>
+
+<p>
+Here's an example of how to chain these methods to create a character sequence
+with different types of styling applied to individual words:
+</p>
+
+<pre style="pretty-print">
+// Create an italic "hello, " a red "world",
+// and bold the entire sequence.
+CharSequence text = bold(italic(res.getString(R.string.hello)),
+    color(Color.RED, res.getString(R.string.world)));
+</pre>
\ No newline at end of file
diff --git a/docs/html/guide/topics/ui/notifiers/notifications.jd b/docs/html/guide/topics/ui/notifiers/notifications.jd
index 3b1292e..59c2269 100644
--- a/docs/html/guide/topics/ui/notifiers/notifications.jd
+++ b/docs/html/guide/topics/ui/notifiers/notifications.jd
@@ -16,6 +16,7 @@
       <li><a href="#Required">Required notification contents</a></li>
       <li><a href="#Optional">Optional notification contents and settings</a></li>
       <li><a href="#Actions">Notification actions</a></li>
+      <li><a href="#Priority">Notification priority</a></li>
       <li><a href="#SimpleNotification">Creating a simple notification</a></li>
       <li><a href="#ApplyStyle">Applying a big view style to a notification</a></li>
       <li><a href="#Compatibility">Handling compatibility</a></li>
@@ -290,6 +291,26 @@
     {@link android.support.v4.app.NotificationCompat.Builder}.
 </p>
 <!-- ------------------------------------------------------------------------------------------ -->
+<h3 id="Priority">Notification priority</h3>
+<p>
+    If you wish, you can set the priority of a notification. The priority acts
+    as a hint to the device UI about how the notification should be displayed.
+    To set a notification's priority, call {@link
+    android.support.v4.app.NotificationCompat.Builder#setPriority(int)
+    NotificationCompat.Builder.setPriority()} and pass in one of the {@link
+    android.support.v4.app.NotificationCompat} priority constants. There are
+    five priority levels, ranging from {@link
+    android.support.v4.app.NotificationCompat#PRIORITY_MIN} (-2) to {@link
+    android.support.v4.app.NotificationCompat#PRIORITY_MAX} (2); if not set, the
+    priority defaults to {@link
+    android.support.v4.app.NotificationCompat#PRIORITY_DEFAULT} (0).
+</p>
+<p> For information about setting an appropriate priority level, see "Correctly
+    set and manage notification priority" in the <a
+    href="{@docRoot}design/patterns/notifications.html">Notifications</a> Design
+    guide.
+</p>
+<!-- ------------------------------------------------------------------------------------------ -->
 <h3 id="SimpleNotification">Creating a simple notification</h3>
 <p>
     The following snippet illustrates a simple notification that specifies an activity to open when
diff --git a/docs/html/wear/images/notif_summary_framed.png b/docs/html/wear/images/notif_summary_framed.png
new file mode 100644
index 0000000..17b1703
--- /dev/null
+++ b/docs/html/wear/images/notif_summary_framed.png
Binary files differ
diff --git a/docs/html/wear/notifications/stacks.jd b/docs/html/wear/notifications/stacks.jd
index 7f955f6..a2d34ce 100644
--- a/docs/html/wear/notifications/stacks.jd
+++ b/docs/html/wear/notifications/stacks.jd
@@ -2,8 +2,8 @@
 
 @jd:body
 
-<img src="{@docRoot}wear/images/11_bundles_B.png" height="200" width="169" style="float:right;margin:0 0 20px 40px" />
-<img src="{@docRoot}wear/images/11_bundles_A.png" height="200" width="169" style="float:right;margin:0 0 20px 40px" />
+<img src="{@docRoot}wear/images/11_bundles_B.png" height="200" width="169" style="float:right;margin:0 0 20px 40px" alt="" />
+<img src="{@docRoot}wear/images/11_bundles_A.png" height="200" width="169" style="float:right;margin:0 0 20px 40px" alt="" />
 
 <p>When creating notifications for a handheld device, you should always aggregate similar
 notifications into a single summary notification. For example, if your app creates notifications
@@ -29,20 +29,44 @@
 
 <p>To create a stack, call <a
 href="{@docRoot}reference/android/preview/support/wearable/notifications/WearableNotifications.Builder.html#setGroup(java.lang.String, int)">
-<code>setGroup()</code></a> for each notification you want in the stack, passing the same
-group key. For example:</p>
+<code>setGroup()</code></a> for each notification you want in the stack and specify a
+group key. Then call <a href="{@docRoot}reference/android/preview/support/v4/app/NotificationManagerCompat.html#notify(int, android.app.Notification)"><code>notify()</code></a> to send it to the wearable.</p>
 
 <pre style="clear:right">
 final static String GROUP_KEY_EMAILS = "group_key_emails";
 
+// Build the notification and pass this builder to WearableNotifications.Builder
 NotificationCompat.Builder builder = new NotificationCompat.Builder(mContext)
-         .setContentTitle("New mail from " + sender)
-         .setContentText(subject)
+         .setContentTitle("New mail from " + sender1)
+         .setContentText(subject1)
          .setSmallIcon(R.drawable.new_mail);
 
-Notification notif = new WearableNotifications.Builder(builder)
+Notification notif1 = new WearableNotifications.Builder(builder)
          .setGroup(GROUP_KEY_EMAILS)
          .build();
+
+// Issue the notification
+NotificationManagerCompat notificationManager =
+        NotificationManagerCompat.from(this);
+notificationManager.notify(notificationId1, notif);
+</pre>
+
+<p>Later on, when you create another notification, specify
+the same group key. When you call <a href="{@docRoot}reference/android/preview/support/v4/app/NotificationManagerCompat.html#notify(int, android.app.Notification)"><code>notify()</code></a>, this notification appears
+in the same stack as the previous notification, instead of as a new card:</p>
+
+<pre style="clear:right">
+builder = new NotificationCompat.Builder(mContext)
+         .setContentTitle("New mail from " + sender2)
+         .setContentText(subject2)
+         .setSmallIcon(R.drawable.new_mail);
+
+// Use the same group as the previous notification
+Notification notif2 = new WearableNotifications.Builder(builder)
+         .setGroup(GROUP_KEY_EMAILS)
+         .build();
+
+notificationManager.notify(notificationId2, notif);
 </pre>
 
 <p>By default, notifications appear in the order in which you added them, with the most recent
@@ -54,19 +78,55 @@
 
 <h2 id="AddSummary">Add a Summary Notification</h2>
 
+<img src="{@docRoot}wear/images/notif_summary_framed.png" height="242" width="330" style="float:right;margin:0 0 20px 40px" alt="" />
+
 <p>It's important that you still provide a summary notification that appears on handheld devices.
 So in addition to adding each unique notification to the same stack group, also add a summary
 notification, but set its order position to be <a
 href="{@docRoot}reference/android/preview/support/wearable/notifications/WearableNotifications.html#GROUP_ORDER_SUMMARY"><code>GROUP_ORDER_SUMMARY</code></a>.</p>
 
-<pre>
-Notification summaryNotification = new WearableNotifications.Builder(builder)
-         .setGroup(GROUP_KEY_EMAILS, WearableNotifications.GROUP_ORDER_SUMMARY)
-         .build();
+<p>This notification does not appear in your stack of notifications on the wearable, but
+appears as the only notification on the handheld device.</p>
+
+<pre style="clear:right">
+Bitmap largeIcon = BitmapFactory.decodeResource(getResources(),
+        R.drawable.ic_large_icon);
+
+builder = new NotificationCompat.Builder(this)
+        .setSmallIcon(R.drawable.ic_small_icon)
+        .setLargeIcon(largeIcon);
+
+// Use the same group key and pass this builder to InboxStyle notification
+WearableNotifications.Builder wearableBuilder = new WearableNotifications
+        .Builder(builder)
+        .setGroup(GROUP_KEY_EMAILS,
+                WearableNotifications.GROUP_ORDER_SUMMARY);
+
+// Build the final notification to show on the handset
+Notification summaryNotification = new NotificationCompat.InboxStyle(
+        wearableBuilder.getCompatBuilder())
+        .addLine("Alex Faaborg   Check this out")
+        .addLine("Jeff Chang   Launch Party")
+        .setBigContentTitle("2 new messages")
+        .setSummaryText("johndoe@gmail.com")
+        .build();
+
+notificationManager.notify(notificationId3, summaryNotification);
 </pre>
 
-<p>This notification will not appear in your stack of notifications on the wearable, but
-appears as the only notification on the handheld device.
+<p>
+This notification uses {@link android.support.v4.app.NotificationCompat.InboxStyle},
+which gives you an easy way to create notifications for email or messaging apps.
+You can use this style, another one defined in {@link android.support.v4.app.NotificationCompat},
+or no style for the summary notification.
+</p>
 
+<p class="note"><b>Tip:</b>
+To style the text like in the example screenshot, see
+<a href="{@docRoot}guide/topics/resources/string-resource.html#StylingWithHTML">Styling
+with HTML markup</a> and
+<a href="{@docRoot}guide/topics/resources/string-resource.html#StylingWithSpannables">Styling
+with Spannables</a>.
+</p>
 </body>
-</html>
+</html>
\ No newline at end of file
diff --git a/graphics/java/android/graphics/CanvasProperty.java b/graphics/java/android/graphics/CanvasProperty.java
index 99ea9b1..be86060 100644
--- a/graphics/java/android/graphics/CanvasProperty.java
+++ b/graphics/java/android/graphics/CanvasProperty.java
@@ -16,12 +16,15 @@
 
 package android.graphics;
 
+import com.android.internal.util.VirtualRefBasePtr;
+
 /**
  * TODO: Make public?
  * @hide
  */
 public final class CanvasProperty<T> {
-    private long mNativeContainer;
+
+    private VirtualRefBasePtr mProperty;
 
     public static CanvasProperty<Float> createFloat(float initialValue) {
         return new CanvasProperty<Float>(nCreateFloat(initialValue));
@@ -32,25 +35,14 @@
     }
 
     private CanvasProperty(long nativeContainer) {
-        mNativeContainer = nativeContainer;
+        mProperty = new VirtualRefBasePtr(nativeContainer);
     }
 
     /** @hide */
     public long getNativeContainer() {
-        return mNativeContainer;
-    }
-
-    @Override
-    protected void finalize() throws Throwable {
-        try {
-            nUnref(mNativeContainer);
-            mNativeContainer = 0;
-        } finally {
-            super.finalize();
-        }
+        return mProperty.get();
     }
 
     private static native long nCreateFloat(float initialValue);
     private static native long nCreatePaint(long initialValuePaintPtr);
-    private static native void nUnref(long ptr);
 }
diff --git a/graphics/java/android/graphics/drawable/AnimatedStateListDrawable.java b/graphics/java/android/graphics/drawable/AnimatedStateListDrawable.java
new file mode 100644
index 0000000..46e3401
--- /dev/null
+++ b/graphics/java/android/graphics/drawable/AnimatedStateListDrawable.java
@@ -0,0 +1,524 @@
+/*
+ * 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.drawable;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
+import android.animation.TimeInterpolator;
+import android.content.res.Resources;
+import android.content.res.Resources.Theme;
+import android.content.res.TypedArray;
+import android.util.AttributeSet;
+import android.util.LongSparseLongArray;
+import android.util.SparseIntArray;
+import android.util.StateSet;
+
+import com.android.internal.R;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+
+/**
+ * Drawable containing a set of Drawable keyframes where the currently displayed
+ * keyframe is chosen based on the current state set. Animations between
+ * keyframes may optionally be defined using transition elements.
+ * <p>
+ * This drawable can be defined in an XML file with the <code>
+ * &lt;animated-selector></code> element. Each keyframe Drawable is defined in a
+ * nested <code>&lt;item></code> element. Transitions are defined in a nested
+ * <code>&lt;transition></code> element.
+ *
+ * @attr ref android.R.styleable#DrawableStates_state_focused
+ * @attr ref android.R.styleable#DrawableStates_state_window_focused
+ * @attr ref android.R.styleable#DrawableStates_state_enabled
+ * @attr ref android.R.styleable#DrawableStates_state_checkable
+ * @attr ref android.R.styleable#DrawableStates_state_checked
+ * @attr ref android.R.styleable#DrawableStates_state_selected
+ * @attr ref android.R.styleable#DrawableStates_state_activated
+ * @attr ref android.R.styleable#DrawableStates_state_active
+ * @attr ref android.R.styleable#DrawableStates_state_single
+ * @attr ref android.R.styleable#DrawableStates_state_first
+ * @attr ref android.R.styleable#DrawableStates_state_middle
+ * @attr ref android.R.styleable#DrawableStates_state_last
+ * @attr ref android.R.styleable#DrawableStates_state_pressed
+ */
+public class AnimatedStateListDrawable extends StateListDrawable {
+    private static final String ELEMENT_TRANSITION = "transition";
+    private static final String ELEMENT_ITEM = "item";
+
+    private AnimatedStateListState mState;
+
+    /** The currently running animation, if any. */
+    private ObjectAnimator mAnim;
+
+    /** Index to be set after the animation ends. */
+    private int mAnimToIndex = -1;
+
+    /** Index away from which we are animating. */
+    private int mAnimFromIndex = -1;
+
+    private boolean mMutated;
+
+    public AnimatedStateListDrawable() {
+        this(null, null);
+    }
+
+    /**
+     * Add a new drawable to the set of keyframes.
+     *
+     * @param stateSet An array of resource IDs to associate with the keyframe
+     * @param drawable The drawable to show when in the specified state
+     * @param id The unique identifier for the keyframe
+     */
+    public void addState(int[] stateSet, Drawable drawable, int id) {
+        if (drawable != null) {
+            mState.addStateSet(stateSet, drawable, id);
+            onStateChange(getState());
+        }
+    }
+
+    /**
+     * Adds a new transition between keyframes.
+     *
+     * @param fromId Unique identifier of the starting keyframe
+     * @param toId Unique identifier of the ending keyframe
+     * @param anim An AnimationDrawable to use as a transition
+     * @param reversible Whether the transition can be reversed
+     */
+    public void addTransition(int fromId, int toId, AnimationDrawable anim, boolean reversible) {
+        mState.addTransition(fromId, toId, anim, reversible);
+    }
+
+    @Override
+    public boolean isStateful() {
+        return true;
+    }
+
+    @Override
+    protected boolean onStateChange(int[] stateSet) {
+        final int keyframeIndex = mState.indexOfKeyframe(stateSet);
+        if (keyframeIndex == getCurrentIndex()) {
+            return false;
+        }
+
+        if (selectTransition(keyframeIndex)) {
+            return true;
+        }
+
+        if (selectDrawable(keyframeIndex)) {
+            return true;
+        }
+
+        return super.onStateChange(stateSet);
+    }
+
+    private boolean selectTransition(int toIndex) {
+        if (mAnim != null) {
+            if (toIndex == mAnimToIndex) {
+                // Already animating to that keyframe.
+                return true;
+            } else if (toIndex == mAnimFromIndex) {
+                // Reverse the current animation.
+                mAnim.reverse();
+                mAnimFromIndex = mAnimToIndex;
+                mAnimToIndex = toIndex;
+                return true;
+            }
+
+            // Changing animation, end the current animation.
+            mAnim.end();
+        }
+
+        final AnimatedStateListState state = mState;
+        final int fromIndex = getCurrentIndex();
+        final int fromId = state.getKeyframeIdAt(fromIndex);
+        final int toId = state.getKeyframeIdAt(toIndex);
+
+        if (toId == 0 || fromId == 0) {
+            // Missing a keyframe ID.
+            return false;
+        }
+
+        final int transitionIndex = state.indexOfTransition(fromId, toId);
+        if (transitionIndex < 0 || !selectDrawable(transitionIndex)) {
+            // Couldn't select a transition.
+            return false;
+        }
+
+        final Drawable d = getCurrent();
+        if (!(d instanceof AnimationDrawable)) {
+            // Transition isn't an animation.
+            return false;
+        }
+
+        final AnimationDrawable ad = (AnimationDrawable) d;
+        final boolean reversed = mState.isTransitionReversed(fromId, toId);
+        final int frameCount = ad.getNumberOfFrames();
+        final int fromFrame = reversed ? frameCount - 1 : 0;
+        final int toFrame = reversed ? 0 : frameCount - 1;
+
+        final FrameInterpolator interp = new FrameInterpolator(ad, reversed);
+        final ObjectAnimator anim = ObjectAnimator.ofInt(ad, "currentIndex", fromFrame, toFrame);
+        anim.setAutoCancel(true);
+        anim.setDuration(interp.getTotalDuration());
+        anim.addListener(mAnimListener);
+        anim.setInterpolator(interp);
+        anim.start();
+
+        mAnim = anim;
+        mAnimFromIndex = fromIndex;
+        mAnimToIndex = toIndex;
+        return true;
+    }
+
+    @Override
+    public void jumpToCurrentState() {
+        super.jumpToCurrentState();
+
+        if (mAnim != null) {
+            mAnim.end();
+        }
+    }
+
+    @Override
+    public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme)
+            throws XmlPullParserException, IOException {
+        final TypedArray a = r.obtainAttributes(attrs, R.styleable.AnimatedStateListDrawable);
+
+        super.inflateWithAttributes(r, parser, a, R.styleable.AnimatedStateListDrawable_visible);
+
+        final StateListState stateListState = getStateListState();
+        stateListState.setVariablePadding(a.getBoolean(
+                R.styleable.AnimatedStateListDrawable_variablePadding, false));
+        stateListState.setConstantSize(a.getBoolean(
+                R.styleable.AnimatedStateListDrawable_constantSize, false));
+        stateListState.setEnterFadeDuration(a.getInt(
+                R.styleable.AnimatedStateListDrawable_enterFadeDuration, 0));
+        stateListState.setExitFadeDuration(a.getInt(
+                R.styleable.AnimatedStateListDrawable_exitFadeDuration, 0));
+
+        setDither(a.getBoolean(R.styleable.AnimatedStateListDrawable_dither, true));
+        setAutoMirrored(a.getBoolean(R.styleable.AnimatedStateListDrawable_autoMirrored, false));
+
+        a.recycle();
+
+        int type;
+
+        final int innerDepth = parser.getDepth() + 1;
+        int depth;
+        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                && ((depth = parser.getDepth()) >= innerDepth
+                || type != XmlPullParser.END_TAG)) {
+            if (type != XmlPullParser.START_TAG) {
+                continue;
+            }
+
+            if (depth > innerDepth) {
+                continue;
+            }
+
+            if (parser.getName().equals(ELEMENT_ITEM)) {
+                parseItem(r, parser, attrs, theme);
+            } else if (parser.getName().equals(ELEMENT_TRANSITION)) {
+                parseTransition(r, parser, attrs, theme);
+            }
+        }
+
+        onStateChange(getState());
+    }
+
+    private int parseTransition(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme)
+            throws XmlPullParserException, IOException {
+        int drawableRes = 0;
+        int fromId = 0;
+        int toId = 0;
+        boolean reversible = false;
+
+        final int numAttrs = attrs.getAttributeCount();
+        for (int i = 0; i < numAttrs; i++) {
+            final int stateResId = attrs.getAttributeNameResource(i);
+            switch (stateResId) {
+                case 0:
+                    break;
+                case R.attr.fromId:
+                    fromId = attrs.getAttributeResourceValue(i, 0);
+                    break;
+                case R.attr.toId:
+                    toId = attrs.getAttributeResourceValue(i, 0);
+                    break;
+                case R.attr.drawable:
+                    drawableRes = attrs.getAttributeResourceValue(i, 0);
+                    break;
+                case R.attr.reversible:
+                    reversible = attrs.getAttributeBooleanValue(i, false);
+                    break;
+            }
+        }
+
+        final Drawable dr;
+        if (drawableRes != 0) {
+            dr = r.getDrawable(drawableRes);
+        } else {
+            int type;
+            while ((type = parser.next()) == XmlPullParser.TEXT) {
+            }
+            if (type != XmlPullParser.START_TAG) {
+                throw new XmlPullParserException(
+                        parser.getPositionDescription()
+                                + ": <item> tag requires a 'drawable' attribute or "
+                                + "child tag defining a drawable");
+            }
+            dr = Drawable.createFromXmlInnerThemed(r, parser, attrs, theme);
+        }
+
+        final AnimationDrawable anim;
+        if (dr instanceof AnimationDrawable) {
+            anim = (AnimationDrawable) dr;
+        } else {
+            throw new XmlPullParserException(parser.getPositionDescription()
+                    + ": <transition> tag requires a 'drawable' attribute or "
+                    + "child tag defining a drawable of type <animation>");
+        }
+
+        return mState.addTransition(fromId, toId, anim, reversible);
+    }
+
+    private int parseItem(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme)
+            throws XmlPullParserException, IOException {
+        int drawableRes = 0;
+        int keyframeId = 0;
+
+        int j = 0;
+        final int numAttrs = attrs.getAttributeCount();
+        int[] states = new int[numAttrs];
+        for (int i = 0; i < numAttrs; i++) {
+            final int stateResId = attrs.getAttributeNameResource(i);
+            switch (stateResId) {
+                case 0:
+                    break;
+                case R.attr.id:
+                    keyframeId = attrs.getAttributeResourceValue(i, 0);
+                    break;
+                case R.attr.drawable:
+                    drawableRes = attrs.getAttributeResourceValue(i, 0);
+                    break;
+                default:
+                    final boolean hasState = attrs.getAttributeBooleanValue(i, false);
+                    states[j++] = hasState ? stateResId : -stateResId;
+            }
+        }
+        states = StateSet.trimStateSet(states, j);
+
+        final Drawable dr;
+        if (drawableRes != 0) {
+            dr = r.getDrawable(drawableRes);
+        } else {
+            int type;
+            while ((type = parser.next()) == XmlPullParser.TEXT) {
+            }
+            if (type != XmlPullParser.START_TAG) {
+                throw new XmlPullParserException(
+                        parser.getPositionDescription()
+                                + ": <item> tag requires a 'drawable' attribute or "
+                                + "child tag defining a drawable");
+            }
+            dr = Drawable.createFromXmlInnerThemed(r, parser, attrs, theme);
+        }
+
+        return mState.addStateSet(states, dr, keyframeId);
+    }
+
+    @Override
+    public Drawable mutate() {
+        if (!mMutated) {
+            final AnimatedStateListState newState = new AnimatedStateListState(mState, this, null);
+            setConstantState(newState);
+            mMutated = true;
+        }
+
+        return this;
+    }
+
+    private final AnimatorListenerAdapter mAnimListener = new AnimatorListenerAdapter() {
+        @Override
+        public void onAnimationEnd(Animator anim) {
+            selectDrawable(mAnimToIndex);
+
+            mAnimToIndex = -1;
+            mAnimFromIndex = -1;
+            mAnim = null;
+        }
+    };
+
+    static class AnimatedStateListState extends StateListState {
+        private static final int REVERSE_SHIFT = 32;
+        private static final int REVERSE_MASK = 0x1;
+
+        final LongSparseLongArray mTransitions;
+        final SparseIntArray mStateIds;
+
+        AnimatedStateListState(AnimatedStateListState orig, AnimatedStateListDrawable owner,
+                Resources res) {
+            super(orig, owner, res);
+
+            if (orig != null) {
+                mTransitions = orig.mTransitions.clone();
+                mStateIds = orig.mStateIds.clone();
+            } else {
+                mTransitions = new LongSparseLongArray();
+                mStateIds = new SparseIntArray();
+            }
+        }
+
+        int addTransition(int fromId, int toId, AnimationDrawable anim, boolean reversible) {
+            final int pos = super.addChild(anim);
+            final long keyFromTo = generateTransitionKey(fromId, toId);
+            mTransitions.append(keyFromTo, pos);
+
+            if (reversible) {
+                final long keyToFrom = generateTransitionKey(toId, fromId);
+                mTransitions.append(keyToFrom, pos | (1L << REVERSE_SHIFT));
+            }
+
+            return addChild(anim);
+        }
+
+        int addStateSet(int[] stateSet, Drawable drawable, int id) {
+            final int index = super.addStateSet(stateSet, drawable);
+            mStateIds.put(index, id);
+            return index;
+        }
+
+        int indexOfKeyframe(int[] stateSet) {
+            final int index = super.indexOfStateSet(stateSet);
+            if (index >= 0) {
+                return index;
+            }
+
+            return super.indexOfStateSet(StateSet.WILD_CARD);
+        }
+
+        int getKeyframeIdAt(int index) {
+            return index < 0 ? 0 : mStateIds.get(index, 0);
+        }
+
+        int indexOfTransition(int fromId, int toId) {
+            final long keyFromTo = generateTransitionKey(fromId, toId);
+            return (int) mTransitions.get(keyFromTo, -1);
+        }
+
+        boolean isTransitionReversed(int fromId, int toId) {
+            final long keyFromTo = generateTransitionKey(fromId, toId);
+            return (mTransitions.get(keyFromTo, -1) >> REVERSE_SHIFT & REVERSE_MASK) == 1;
+        }
+
+        @Override
+        public Drawable newDrawable() {
+            return new AnimatedStateListDrawable(this, null);
+        }
+
+        @Override
+        public Drawable newDrawable(Resources res) {
+            return new AnimatedStateListDrawable(this, res);
+        }
+
+        private static long generateTransitionKey(int fromId, int toId) {
+            return (long) fromId << 32 | toId;
+        }
+    }
+
+    void setConstantState(AnimatedStateListState state) {
+        super.setConstantState(state);
+
+        mState = state;
+    }
+
+    private AnimatedStateListDrawable(AnimatedStateListState state, Resources res) {
+        super(null);
+
+        final AnimatedStateListState newState = new AnimatedStateListState(state, this, res);
+        setConstantState(newState);
+        onStateChange(getState());
+        jumpToCurrentState();
+    }
+
+    /**
+     * Interpolates between frames with respect to their individual durations.
+     */
+    private static class FrameInterpolator implements TimeInterpolator {
+        private int[] mFrameTimes;
+        private int mFrames;
+        private int mTotalDuration;
+
+        public FrameInterpolator(AnimationDrawable d, boolean reversed) {
+            updateFrames(d, reversed);
+        }
+
+        public int updateFrames(AnimationDrawable d, boolean reversed) {
+            final int N = d.getNumberOfFrames();
+            mFrames = N;
+
+            if (mFrameTimes == null || mFrameTimes.length < N) {
+                mFrameTimes = new int[N];
+            }
+
+            final int[] frameTimes = mFrameTimes;
+            int totalDuration = 0;
+            for (int i = 0; i < N; i++) {
+                final int duration = d.getDuration(reversed ? N - i - 1 : i);
+                frameTimes[i] = duration;
+                totalDuration += duration;
+            }
+
+            mTotalDuration = totalDuration;
+            return totalDuration;
+        }
+
+        public int getTotalDuration() {
+            return mTotalDuration;
+        }
+
+        @Override
+        public float getInterpolation(float input) {
+            final int elapsed = (int) (input * mTotalDuration + 0.5f);
+            final int N = mFrames;
+            final int[] frameTimes = mFrameTimes;
+
+            // Find the current frame and remaining time within that frame.
+            int remaining = elapsed;
+            int i = 0;
+            while (i < N && remaining >= frameTimes[i]) {
+                remaining -= frameTimes[i];
+                i++;
+            }
+
+            // Remaining time is relative of total duration.
+            final float frameElapsed;
+            if (i < N) {
+                frameElapsed = remaining / (float) mTotalDuration;
+            } else {
+                frameElapsed = 0;
+            }
+
+            return i / (float) N + frameElapsed;
+        }
+    }
+}
+
diff --git a/graphics/java/android/graphics/drawable/AnimationDrawable.java b/graphics/java/android/graphics/drawable/AnimationDrawable.java
index 3f94e26..da4bc10 100644
--- a/graphics/java/android/graphics/drawable/AnimationDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimationDrawable.java
@@ -94,7 +94,7 @@
         boolean changed = super.setVisible(visible, restart);
         if (visible) {
             if (changed || restart) {
-                setFrame(0, true, true);
+                setFrame(0, true, mCurFrame >= 0);
             }
         } else {
             unscheduleSelf(this);
diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java
index b9d5e19..b939636 100644
--- a/graphics/java/android/graphics/drawable/Drawable.java
+++ b/graphics/java/android/graphics/drawable/Drawable.java
@@ -1039,6 +1039,8 @@
         final String name = parser.getName();
         if (name.equals("selector")) {
             drawable = new StateListDrawable();
+        } else if (name.equals("animated-selector")) {
+            drawable = new AnimatedStateListDrawable();
         } else if (name.equals("level-list")) {
             drawable = new LevelListDrawable();
         } else if (name.equals("layer-list")) {
diff --git a/graphics/java/android/graphics/drawable/DrawableContainer.java b/graphics/java/android/graphics/drawable/DrawableContainer.java
index 1f8b51d..08fc99d 100644
--- a/graphics/java/android/graphics/drawable/DrawableContainer.java
+++ b/graphics/java/android/graphics/drawable/DrawableContainer.java
@@ -359,6 +359,16 @@
                 mDrawableContainerState.getOpacity();
     }
 
+    /** @hide */
+    public void setCurrentIndex(int index) {
+        selectDrawable(index);
+    }
+
+    /** @hide */
+    public int getCurrentIndex() {
+        return mCurIndex;
+    }
+
     public boolean selectDrawable(int idx) {
         if (idx == mCurIndex) {
             return false;
diff --git a/graphics/java/android/graphics/drawable/StateListDrawable.java b/graphics/java/android/graphics/drawable/StateListDrawable.java
index 271af2b..f22a063 100644
--- a/graphics/java/android/graphics/drawable/StateListDrawable.java
+++ b/graphics/java/android/graphics/drawable/StateListDrawable.java
@@ -55,8 +55,9 @@
  * @attr ref android.R.styleable#DrawableStates_state_pressed
  */
 public class StateListDrawable extends DrawableContainer {
+    private static final String TAG = StateListDrawable.class.getSimpleName();
+
     private static final boolean DEBUG = false;
-    private static final String TAG = "StateListDrawable";
 
     /**
      * To be proper, we should have a getter for dither (and alpha, etc.)
@@ -69,7 +70,8 @@
      * to improve the quality at negligible cost.
      */
     private static final boolean DEFAULT_DITHER = true;
-    private final StateListState mStateListState;
+
+    private StateListState mStateListState;
     private boolean mMutated;
 
     public StateListDrawable() {
@@ -274,7 +276,7 @@
         mStateListState.setLayoutDirection(layoutDirection);
     }
 
-    static final class StateListState extends DrawableContainerState {
+    static class StateListState extends DrawableContainerState {
         int[][] mStateSets;
 
         StateListState(StateListState orig, StateListDrawable owner, Resources res) {
@@ -293,7 +295,7 @@
             return pos;
         }
 
-        private int indexOfStateSet(int[] stateSet) {
+        int indexOfStateSet(int[] stateSet) {
             final int[][] stateSets = mStateSets;
             final int N = getChildCount();
             for (int i = 0; i < N; i++) {
@@ -323,11 +325,26 @@
         }
     }
 
+    void setConstantState(StateListState state) {
+        super.setConstantState(state);
+
+        mStateListState = state;
+    }
+
     private StateListDrawable(StateListState state, Resources res) {
-        StateListState as = new StateListState(state, this, res);
-        mStateListState = as;
-        setConstantState(as);
+        final StateListState newState = new StateListState(state, this, res);
+        setConstantState(newState);
         onStateChange(getState());
     }
+
+    /**
+     * This constructor exists so subclasses can avoid calling the default
+     * constructor and setting up a StateListDrawable-specific constant state.
+     */
+    StateListDrawable(StateListState state) {
+        if (state != null) {
+            setConstantState(state);
+        }
+    }
 }
 
diff --git a/graphics/java/android/graphics/drawable/VectorDrawable.java b/graphics/java/android/graphics/drawable/VectorDrawable.java
index ff4ab98..2da86154 100644
--- a/graphics/java/android/graphics/drawable/VectorDrawable.java
+++ b/graphics/java/android/graphics/drawable/VectorDrawable.java
@@ -46,7 +46,7 @@
  * This lets you create a drawable based on an XML vector graphic It can be
  * defined in an XML file with the <code>&lt;vector></code> element.
  * <p/>
- * The vector drawable has 6 elements:
+ * The vector drawable has the following elements:
  * <p/>
  * <dl>
  * <dt><code>&lt;vector></code></dt>
@@ -59,15 +59,15 @@
  * <dd>Used to defined the size of the virtual canvas the paths are drawn on.
  * The size is defined using the attributes <code>android:viewportHeight</code>
  * <code>android:viewportWidth</code></dd>
- * <dt><code>&lt;group></code></dt>
- * <dd>Defines the static 2D image.</dd>
  * <dt><code>&lt;path></code></dt>
- * <dd>Defines paths to be drawn. The path elements must be within a group
+ * <dd>Defines paths to be drawn. Multiple paths can be defined in one xml file.
+ * The paths are drawn in the order of their definition order.
  * <dl>
  * <dt><code>android:name</code>
  * <dd>Defines the name of the path.</dd></dt>
  * <dt><code>android:pathData</code>
- * <dd>Defines path string.</dd></dt>
+ * <dd>Defines path string. This is using exactly same format as "d" attribute
+ * in the SVG's path data</dd></dt>
  * <dt><code>android:fill</code>
  * <dd>Defines the color to fill the path (none if not present).</dd></dt>
  * <dt><code>android:stroke</code>
@@ -108,7 +108,6 @@
 
     private static final String SHAPE_SIZE = "size";
     private static final String SHAPE_VIEWPORT = "viewport";
-    private static final String SHAPE_GROUP = "group";
     private static final String SHAPE_PATH = "path";
     private static final String SHAPE_VECTOR = "vector";
 
@@ -266,10 +265,9 @@
 
         boolean noSizeTag = true;
         boolean noViewportTag = true;
-        boolean noGroupTag = true;
         boolean noPathTag = true;
 
-        VGroup currentGroup = null;
+        VGroup currentGroup = new VGroup();
 
         int eventType = parser.getEventType();
         while (eventType != XmlPullParser.END_DOCUMENT) {
@@ -286,10 +284,6 @@
                 } else if (SHAPE_VIEWPORT.equals(tagName)) {
                     pathRenderer.parseViewport(res, attrs);
                     noViewportTag = false;
-                } else if (SHAPE_GROUP.equals(tagName)) {
-                    currentGroup = new VGroup();
-                    pathRenderer.mGroupList.add(currentGroup);
-                    noGroupTag = false;
                 } else if (SHAPE_VECTOR.equals(tagName)) {
                     final TypedArray a = res.obtainAttributes(attrs, R.styleable.VectorDrawable);
 
@@ -310,7 +304,7 @@
             eventType = parser.next();
         }
 
-        if (noSizeTag || noViewportTag || noGroupTag || noPathTag) {
+        if (noSizeTag || noViewportTag || noPathTag) {
             final StringBuffer tag = new StringBuffer();
 
             if (noSizeTag) {
@@ -324,13 +318,6 @@
                 tag.append(SHAPE_SIZE);
             }
 
-            if (noGroupTag) {
-                if (tag.length() > 0) {
-                    tag.append(" & ");
-                }
-                tag.append(SHAPE_GROUP);
-            }
-
             if (noPathTag) {
                 if (tag.length() > 0) {
                     tag.append(" or ");
@@ -341,6 +328,7 @@
             throw new XmlPullParserException("no " + tag + " defined");
         }
 
+        pathRenderer.mCurrentGroup = currentGroup;
         // post parse cleanup
         pathRenderer.parseFinish();
         return pathRenderer;
@@ -394,7 +382,7 @@
         private Paint mFillPaint;
         private PathMeasure mPathMeasure;
 
-        final ArrayList<VGroup> mGroupList = new ArrayList<VGroup>();
+        private VGroup mCurrentGroup = new VGroup();
 
         float mBaseWidth = 1;
         float mBaseHeight = 1;
@@ -405,7 +393,7 @@
         }
 
         public VPathRenderer(VPathRenderer copy) {
-            mGroupList.addAll(copy.mGroupList);
+            mCurrentGroup = copy.mCurrentGroup;
             if (copy.mCurrentPaths != null) {
                 mCurrentPaths = new VPath[copy.mCurrentPaths.length];
                 for (int i = 0; i < mCurrentPaths.length; i++) {
@@ -420,32 +408,24 @@
         }
 
         public boolean canApplyTheme() {
-            final ArrayList<VGroup> groups = mGroupList;
-            for (int i = groups.size() - 1; i >= 0; i--) {
-                final ArrayList<VPath> paths = groups.get(i).mVGList;
-                for (int j = paths.size() - 1; j >= 0; j--) {
-                    final VPath path = paths.get(j);
-                    if (path.canApplyTheme()) {
-                        return true;
-                    }
+            final ArrayList<VPath> paths = mCurrentGroup.mVGList;
+            for (int j = paths.size() - 1; j >= 0; j--) {
+                final VPath path = paths.get(j);
+                if (path.canApplyTheme()) {
+                    return true;
                 }
             }
-
             return false;
         }
 
         public void applyTheme(Theme t) {
-            final ArrayList<VGroup> groups = mGroupList;
-            for (int i = groups.size() - 1; i >= 0; i--) {
-                final ArrayList<VPath> paths = groups.get(i).mVGList;
-                for (int j = paths.size() - 1; j >= 0; j--) {
-                    final VPath path = paths.get(j);
-                    if (path.canApplyTheme()) {
-                        path.applyTheme(t);
-                    }
+            final ArrayList<VPath> paths = mCurrentGroup.mVGList;
+            for (int j = paths.size() - 1; j >= 0; j--) {
+                final VPath path = paths.get(j);
+                if (path.canApplyTheme()) {
+                    path.applyTheme(t);
                 }
             }
-
         }
 
         public void draw(Canvas canvas, int w, int h) {
@@ -537,11 +517,11 @@
         }
 
         /**
-         * Build the "current" path based on the first group
+         * Build the "current" path based on the current group
          * TODO: improve memory use & performance or move to C++
          */
         public void parseFinish() {
-            final Collection<VPath> paths = mGroupList.get(0).getPaths();
+            final Collection<VPath> paths = mCurrentGroup.getPaths();
             mCurrentPaths = paths.toArray(new VPath[paths.size()]);
             for (int i = 0; i < mCurrentPaths.length; i++) {
                 mCurrentPaths[i] = new VPath(mCurrentPaths[i]);
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index 098753b..6aad5fb 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -2452,15 +2452,19 @@
 
     if (mcc != 0) {
         if (res.size() > 0) res.append("-");
-        res.appendFormat("%dmcc", dtohs(mcc));
+        res.appendFormat("mcc%d", dtohs(mcc));
     }
     if (mnc != 0) {
         if (res.size() > 0) res.append("-");
-        res.appendFormat("%dmnc", dtohs(mnc));
+        res.appendFormat("mnc%d", dtohs(mnc));
     }
+
     char localeStr[RESTABLE_MAX_LOCALE_LEN];
     getBcp47Locale(localeStr);
-    res.append(localeStr);
+    if (strlen(localeStr) > 0) {
+        if (res.size() > 0) res.append("-");
+        res.append(localeStr);
+    }
 
     if ((screenLayout&MASK_LAYOUTDIR) != 0) {
         if (res.size() > 0) res.append("-");
@@ -2627,6 +2631,20 @@
                 break;
         }
     }
+    if ((inputFlags&MASK_KEYSHIDDEN) != 0) {
+        if (res.size() > 0) res.append("-");
+        switch (inputFlags&MASK_KEYSHIDDEN) {
+            case ResTable_config::KEYSHIDDEN_NO:
+                res.append("keysexposed");
+                break;
+            case ResTable_config::KEYSHIDDEN_YES:
+                res.append("keyshidden");
+                break;
+            case ResTable_config::KEYSHIDDEN_SOFT:
+                res.append("keyssoft");
+                break;
+        }
+    }
     if (keyboard != KEYBOARD_ANY) {
         if (res.size() > 0) res.append("-");
         switch (keyboard) {
@@ -2644,17 +2662,18 @@
                 break;
         }
     }
-    if ((inputFlags&MASK_KEYSHIDDEN) != 0) {
+    if ((inputFlags&MASK_NAVHIDDEN) != 0) {
         if (res.size() > 0) res.append("-");
-        switch (inputFlags&MASK_KEYSHIDDEN) {
-            case ResTable_config::KEYSHIDDEN_NO:
-                res.append("keysexposed");
+        switch (inputFlags&MASK_NAVHIDDEN) {
+            case ResTable_config::NAVHIDDEN_NO:
+                res.append("navexposed");
                 break;
-            case ResTable_config::KEYSHIDDEN_YES:
-                res.append("keyshidden");
+            case ResTable_config::NAVHIDDEN_YES:
+                res.append("navhidden");
                 break;
-            case ResTable_config::KEYSHIDDEN_SOFT:
-                res.append("keyssoft");
+            default:
+                res.appendFormat("inputFlagsNavHidden=%d",
+                        dtohs(inputFlags&MASK_NAVHIDDEN));
                 break;
         }
     }
@@ -2678,21 +2697,6 @@
                 break;
         }
     }
-    if ((inputFlags&MASK_NAVHIDDEN) != 0) {
-        if (res.size() > 0) res.append("-");
-        switch (inputFlags&MASK_NAVHIDDEN) {
-            case ResTable_config::NAVHIDDEN_NO:
-                res.append("navsexposed");
-                break;
-            case ResTable_config::NAVHIDDEN_YES:
-                res.append("navhidden");
-                break;
-            default:
-                res.appendFormat("inputFlagsNavHidden=%d",
-                        dtohs(inputFlags&MASK_NAVHIDDEN));
-                break;
-        }
-    }
     if (screenSize != 0) {
         if (res.size() > 0) res.append("-");
         res.appendFormat("%dx%d", dtohs(screenWidth), dtohs(screenHeight));
@@ -5503,7 +5507,25 @@
         if (package == NULL) {
             return (mError=NO_MEMORY);
         }
-        
+
+        if (idmap_id == 0) {
+            err = package->typeStrings.setTo(base+dtohl(pkg->typeStrings),
+                                           header->dataEnd-(base+dtohl(pkg->typeStrings)));
+            if (err != NO_ERROR) {
+                delete group;
+                delete package;
+                return (mError=err);
+            }
+
+            err = package->keyStrings.setTo(base+dtohl(pkg->keyStrings),
+                                          header->dataEnd-(base+dtohl(pkg->keyStrings)));
+            if (err != NO_ERROR) {
+                delete group;
+                delete package;
+                return (mError=err);
+            }
+        }
+
         if (id == 0) {
             // This is a library so assign an ID
             id = mNextPackageId++;
@@ -5521,21 +5543,6 @@
                 return (mError=NO_MEMORY);
             }
 
-            err = package->typeStrings.setTo(base+dtohl(pkg->typeStrings),
-                                           header->dataEnd-(base+dtohl(pkg->typeStrings)));
-            if (err != NO_ERROR) {
-                delete group;
-                delete package;
-                return (mError=err);
-            }
-            err = package->keyStrings.setTo(base+dtohl(pkg->keyStrings),
-                                          header->dataEnd-(base+dtohl(pkg->keyStrings)));
-            if (err != NO_ERROR) {
-                delete group;
-                delete package;
-                return (mError=err);
-            }
-
             //printf("Adding new package id %d at index %d\n", id, idx);
             err = mPackageGroups.add(group);
             if (err < NO_ERROR) {
diff --git a/libs/hwui/Animator.h b/libs/hwui/Animator.h
index 0b074cc..52a1807 100644
--- a/libs/hwui/Animator.h
+++ b/libs/hwui/Animator.h
@@ -17,13 +17,13 @@
 #define ANIMATOR_H
 
 #include <cutils/compiler.h>
+#include <utils/RefBase.h>
 #include <utils/StrongPointer.h>
 
 #include "CanvasProperty.h"
 #include "Interpolator.h"
 #include "TreeInfo.h"
 #include "utils/Macros.h"
-#include "utils/VirtualLightRefBase.h"
 
 namespace android {
 namespace uirenderer {
@@ -45,6 +45,7 @@
 
     ANDROID_API void setInterpolator(Interpolator* interpolator);
     ANDROID_API void setDuration(nsecs_t durationInMs);
+    ANDROID_API nsecs_t duration() { return mDuration; }
     ANDROID_API void setListener(AnimationListener* listener) {
         mListener = listener;
     }
diff --git a/libs/hwui/CanvasProperty.h b/libs/hwui/CanvasProperty.h
index 2e1d176..6074394 100644
--- a/libs/hwui/CanvasProperty.h
+++ b/libs/hwui/CanvasProperty.h
@@ -16,8 +16,9 @@
 #ifndef CANVASPROPERTY_H
 #define CANVASPROPERTY_H
 
+#include <utils/RefBase.h>
+
 #include "utils/Macros.h"
-#include "utils/VirtualLightRefBase.h"
 
 #include <SkPaint.h>
 
diff --git a/libs/hwui/DisplayList.h b/libs/hwui/DisplayList.h
index eaeb772..b2ead5b 100644
--- a/libs/hwui/DisplayList.h
+++ b/libs/hwui/DisplayList.h
@@ -41,7 +41,6 @@
 #include "Matrix.h"
 #include "DeferredDisplayList.h"
 #include "RenderProperties.h"
-#include "utils/VirtualLightRefBase.h"
 
 class SkBitmap;
 class SkPaint;
diff --git a/libs/hwui/DisplayListRenderer.cpp b/libs/hwui/DisplayListRenderer.cpp
index c2ce6ed..2391e80 100644
--- a/libs/hwui/DisplayListRenderer.cpp
+++ b/libs/hwui/DisplayListRenderer.cpp
@@ -58,7 +58,7 @@
 
 void DisplayListRenderer::setViewport(int width, int height) {
     // TODO: DisplayListRenderer shouldn't have a projection matrix, as it should never be used
-    mViewProjMatrix.loadOrtho(0, width, height, 0, -1, 1);
+    mProjectionMatrix.loadOrtho(0, width, height, 0, -1, 1);
 
     initializeViewport(width, height);
 }
diff --git a/libs/hwui/Interpolator.cpp b/libs/hwui/Interpolator.cpp
index 004ddf5..1f84b86 100644
--- a/libs/hwui/Interpolator.cpp
+++ b/libs/hwui/Interpolator.cpp
@@ -13,9 +13,15 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
+#define LOG_TAG "Interpolator"
+
 #include "Interpolator.h"
 
-#include <math.h>
+#include <cmath>
+#include <cutils/log.h>
+
+#include "utils/MathUtils.h"
 
 namespace android {
 namespace uirenderer {
@@ -28,5 +34,90 @@
     return (float)(cosf((input + 1) * M_PI) / 2.0f) + 0.5f;
 }
 
+float AccelerateInterpolator::interpolate(float input) {
+    if (mFactor == 1.0f) {
+        return input * input;
+    } else {
+        return pow(input, mDoubleFactor);
+    }
+}
+
+float AnticipateInterpolator::interpolate(float t) {
+    return t * t * ((mTension + 1) * t - mTension);
+}
+
+static float a(float t, float s) {
+    return t * t * ((s + 1) * t - s);
+}
+
+static float o(float t, float s) {
+    return t * t * ((s + 1) * t + s);
+}
+
+float AnticipateOvershootInterpolator::interpolate(float t) {
+    if (t < 0.5f) return 0.5f * a(t * 2.0f, mTension);
+    else return 0.5f * (o(t * 2.0f - 2.0f, mTension) + 2.0f);
+}
+
+static float bounce(float t) {
+    return t * t * 8.0f;
+}
+
+float BounceInterpolator::interpolate(float t) {
+    t *= 1.1226f;
+    if (t < 0.3535f) return bounce(t);
+    else if (t < 0.7408f) return bounce(t - 0.54719f) + 0.7f;
+    else if (t < 0.9644f) return bounce(t - 0.8526f) + 0.9f;
+    else return bounce(t - 1.0435f) + 0.95f;
+}
+
+float CycleInterpolator::interpolate(float input) {
+    return sinf(2 * mCycles * M_PI * input);
+}
+
+float DecelerateInterpolator::interpolate(float input) {
+    float result;
+    if (mFactor == 1.0f) {
+        result = 1.0f - (1.0f - input) * (1.0f - input);
+    } else {
+        result = 1.0f - pow((1.0f - input), 2 * mFactor);
+    }
+    return result;
+}
+
+float OvershootInterpolator::interpolate(float t) {
+    t -= 1.0f;
+    return t * t * ((mTension + 1) * t + mTension) + 1.0f;
+}
+
+LUTInterpolator::LUTInterpolator(float* values, size_t size) {
+    mValues = values;
+    mSize = size;
+}
+
+LUTInterpolator::~LUTInterpolator() {
+    delete mValues;
+    mValues = 0;
+}
+
+float LUTInterpolator::interpolate(float input) {
+    float lutpos = input * mSize;
+    if (lutpos >= (mSize - 1)) {
+        return mValues[mSize - 1];
+    }
+
+    float ipart, weight;
+    weight = modff(lutpos, &ipart);
+
+    int i1 = (int) ipart;
+    int i2 = MathUtils::min(i1 + 1, mSize - 1);
+
+    float v1 = mValues[i1];
+    float v2 = mValues[i2];
+
+    return MathUtils::lerp(v1, v2, weight);
+}
+
+
 } /* namespace uirenderer */
 } /* namespace android */
diff --git a/libs/hwui/Interpolator.h b/libs/hwui/Interpolator.h
index 2cfb60c..dfa0a85 100644
--- a/libs/hwui/Interpolator.h
+++ b/libs/hwui/Interpolator.h
@@ -16,6 +16,10 @@
 #ifndef INTERPOLATOR_H
 #define INTERPOLATOR_H
 
+#include <stddef.h>
+
+#include <cutils/compiler.h>
+
 namespace android {
 namespace uirenderer {
 
@@ -31,12 +35,80 @@
     Interpolator() {}
 };
 
-class AccelerateDecelerateInterpolator : public Interpolator {
+class ANDROID_API AccelerateDecelerateInterpolator : public Interpolator {
 public:
-    AccelerateDecelerateInterpolator() {}
-    virtual ~AccelerateDecelerateInterpolator() {}
+    virtual float interpolate(float input);
+};
+
+class ANDROID_API AccelerateInterpolator : public Interpolator {
+public:
+    AccelerateInterpolator(float factor) : mFactor(factor), mDoubleFactor(factor*2) {}
+    virtual float interpolate(float input);
+private:
+    const float mFactor;
+    const float mDoubleFactor;
+};
+
+class ANDROID_API AnticipateInterpolator : public Interpolator {
+public:
+    AnticipateInterpolator(float tension) : mTension(tension) {}
+    virtual float interpolate(float input);
+private:
+    const float mTension;
+};
+
+class ANDROID_API AnticipateOvershootInterpolator : public Interpolator {
+public:
+    AnticipateOvershootInterpolator(float tension) : mTension(tension) {}
+    virtual float interpolate(float input);
+private:
+    const float mTension;
+};
+
+class ANDROID_API BounceInterpolator : public Interpolator {
+public:
+    virtual float interpolate(float input);
+};
+
+class ANDROID_API CycleInterpolator : public Interpolator {
+public:
+    CycleInterpolator(float cycles) : mCycles(cycles) {}
+    virtual float interpolate(float input);
+private:
+    const float mCycles;
+};
+
+class ANDROID_API DecelerateInterpolator : public Interpolator {
+public:
+    DecelerateInterpolator(float factor) : mFactor(factor) {}
+    virtual float interpolate(float input);
+private:
+    const float mFactor;
+};
+
+class ANDROID_API LinearInterpolator : public Interpolator {
+public:
+    virtual float interpolate(float input) { return input; }
+};
+
+class ANDROID_API OvershootInterpolator : public Interpolator {
+public:
+    OvershootInterpolator(float tension) : mTension(tension) {}
+    virtual float interpolate(float input);
+private:
+    const float mTension;
+};
+
+class ANDROID_API LUTInterpolator : public Interpolator {
+public:
+    LUTInterpolator(float* values, size_t size);
+    ~LUTInterpolator();
 
     virtual float interpolate(float input);
+
+private:
+    float* mValues;
+    size_t mSize;
 };
 
 } /* namespace uirenderer */
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 5a977c8..20b038d 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -171,7 +171,7 @@
 }
 
 void OpenGLRenderer::initViewport(int width, int height) {
-    mViewProjMatrix.loadOrtho(0, width, height, 0, -1, 1);
+    mProjectionMatrix.loadOrtho(0, width, height, 0, -1, 1);
 
     initializeViewport(width, height);
 }
@@ -628,7 +628,7 @@
     if (restoreOrtho) {
         const Rect& r = restored.viewport;
         glViewport(r.left, r.top, r.right, r.bottom);
-        mViewProjMatrix.load(removed.orthoMatrix); // TODO: should ortho be stored in 'restored'?
+        mProjectionMatrix.load(removed.orthoMatrix); // TODO: should ortho be stored in 'restored'?
     }
 
     if (restoreClip) {
@@ -854,7 +854,7 @@
     mSnapshot->resetClip(clip.left, clip.top, clip.right, clip.bottom);
     mSnapshot->viewport.set(0.0f, 0.0f, bounds.getWidth(), bounds.getHeight());
     mSnapshot->height = bounds.getHeight();
-    mSnapshot->orthoMatrix.load(mViewProjMatrix);
+    mSnapshot->orthoMatrix.load(mProjectionMatrix);
 
     endTiling();
     debugOverdraw(false, false);
@@ -884,8 +884,7 @@
     // Change the ortho projection
     glViewport(0, 0, bounds.getWidth(), bounds.getHeight());
 
-    // TODO: determine best way to support 3d drawing within HW layers
-    mViewProjMatrix.loadOrtho(0.0f, bounds.getWidth(), bounds.getHeight(), 0.0f, -1.0f, 1.0f);
+    mProjectionMatrix.loadOrtho(0.0f, bounds.getWidth(), bounds.getHeight(), 0.0f, -1.0f, 1.0f);
 
     return true;
 }
@@ -1689,17 +1688,17 @@
 
 void OpenGLRenderer::setupDrawModelView(ModelViewMode mode, bool offset,
         float left, float top, float right, float bottom, bool ignoreTransform) {
-    mModelView.loadTranslate(left, top, 0.0f);
+    mModelViewMatrix.loadTranslate(left, top, 0.0f);
     if (mode == kModelViewMode_TranslateAndScale) {
-        mModelView.scale(right - left, bottom - top, 1.0f);
+        mModelViewMatrix.scale(right - left, bottom - top, 1.0f);
     }
 
     bool dirty = right - left > 0.0f && bottom - top > 0.0f;
     if (!ignoreTransform) {
-        mCaches.currentProgram->set(mViewProjMatrix, mModelView, *currentTransform(), offset);
+        mCaches.currentProgram->set(mProjectionMatrix, mModelViewMatrix, *currentTransform(), offset);
         if (dirty && mTrackDirtyRegions) dirtyLayer(left, top, right, bottom, *currentTransform());
     } else {
-        mCaches.currentProgram->set(mViewProjMatrix, mModelView, mat4::identity(), offset);
+        mCaches.currentProgram->set(mProjectionMatrix, mModelViewMatrix, mat4::identity(), offset);
         if (dirty && mTrackDirtyRegions) dirtyLayer(left, top, right, bottom);
     }
 }
@@ -1724,11 +1723,11 @@
             // compensate.
             mat4 modelViewWithoutTransform;
             modelViewWithoutTransform.loadInverse(*currentTransform());
-            modelViewWithoutTransform.multiply(mModelView);
-            mModelView.load(modelViewWithoutTransform);
+            modelViewWithoutTransform.multiply(mModelViewMatrix);
+            mModelViewMatrix.load(modelViewWithoutTransform);
         }
         mDrawModifiers.mShader->setupProgram(mCaches.currentProgram,
-                mModelView, *mSnapshot, &mTextureUnit);
+                mModelViewMatrix, *mSnapshot, &mTextureUnit);
     }
 }
 
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index 1d46945..4f7f01e 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -930,8 +930,8 @@
      */
     Texture* getTexture(const SkBitmap* bitmap);
 
-    // Matrix used for view/projection in shaders
-    mat4 mViewProjMatrix;
+    // Ortho matrix used for projection in shaders
+    mat4 mProjectionMatrix;
 
     /**
      * Model-view matrix used to position/size objects
@@ -939,15 +939,15 @@
      * Stores operation-local modifications to the draw matrix that aren't incorporated into the
      * currentTransform().
      *
-     * If generated with kModelViewMode_Translate, the mModelView will reflect an x/y offset,
+     * If generated with kModelViewMode_Translate, mModelViewMatrix will reflect an x/y offset,
      * e.g. the offset in drawLayer(). If generated with kModelViewMode_TranslateAndScale,
-     * mModelView will reflect a translation and scale, e.g. the translation and scale required to
-     * make VBO 0 (a rect of (0,0,1,1)) scaled to match the x,y offset, and width/height of a
-     * bitmap.
+     * mModelViewMatrix will reflect a translation and scale, e.g. the translation and scale
+     * required to make VBO 0 (a rect of (0,0,1,1)) scaled to match the x,y offset, and width/height
+     * of a bitmap.
      *
      * Used as input to SkiaShader transformation.
      */
-    mat4 mModelView;
+    mat4 mModelViewMatrix;
 
     // State used to define the clipping region
     Rect mTilingClip;
diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h
index 159903c..bc62ee1 100644
--- a/libs/hwui/RenderNode.h
+++ b/libs/hwui/RenderNode.h
@@ -45,7 +45,6 @@
 #include "DisplayList.h"
 #include "RenderProperties.h"
 #include "TreeInfo.h"
-#include "utils/VirtualLightRefBase.h"
 
 class SkBitmap;
 class SkPaint;
diff --git a/libs/hwui/font/Font.cpp b/libs/hwui/font/Font.cpp
index d22cb8a..08e9a1a 100644
--- a/libs/hwui/font/Font.cpp
+++ b/libs/hwui/font/Font.cpp
@@ -214,18 +214,28 @@
     int dstY = y + glyph->mBitmapTop;
 
     CacheTexture* cacheTexture = glyph->mCacheTexture;
-
-    uint32_t cacheWidth = cacheTexture->getWidth();
-    uint32_t startY = glyph->mStartY * cacheWidth;
-    uint32_t endY = startY + (glyph->mBitmapHeight * cacheWidth);
-
     PixelBuffer* pixelBuffer = cacheTexture->getPixelBuffer();
+
+    uint32_t formatSize = PixelBuffer::formatSize(pixelBuffer->getFormat());
+    uint32_t cacheWidth = cacheTexture->getWidth();
+    uint32_t srcStride = formatSize * cacheWidth;
+    uint32_t startY = glyph->mStartY * srcStride;
+    uint32_t endY = startY + (glyph->mBitmapHeight * srcStride);
+
     const uint8_t* cacheBuffer = pixelBuffer->map();
 
     for (uint32_t cacheY = startY, bitmapY = dstY * bitmapWidth; cacheY < endY;
-            cacheY += cacheWidth, bitmapY += bitmapWidth) {
-        memcpy(&bitmap[bitmapY + dstX], &cacheBuffer[cacheY + glyph->mStartX], glyph->mBitmapWidth);
+            cacheY += srcStride, bitmapY += bitmapWidth) {
+
+        if (formatSize == 1) {
+            memcpy(&bitmap[bitmapY + dstX], &cacheBuffer[cacheY + glyph->mStartX], glyph->mBitmapWidth);
+        } else {
+            for (uint32_t i = 0; i < glyph->mBitmapWidth; ++i) {
+                bitmap[bitmapY + dstX + i] = cacheBuffer[cacheY + (glyph->mStartX + i)*formatSize];
+            }
+        }
     }
+
 }
 
 void Font::drawCachedGlyph(CachedGlyphInfo* glyph, float x, float hOffset, float vOffset,
diff --git a/libs/hwui/utils/MathUtils.h b/libs/hwui/utils/MathUtils.h
index 8ba44dc..1a7082b 100644
--- a/libs/hwui/utils/MathUtils.h
+++ b/libs/hwui/utils/MathUtils.h
@@ -33,6 +33,14 @@
     inline static bool isPositive(float value) {
         return value >= gNonZeroEpsilon;
     }
+
+    inline static int min(int a, int b) {
+        return a < b ? a : b;
+    }
+
+    inline static float lerp(float v1, float v2, float t) {
+        return v1 + ((v2 - v1) * t);
+    }
 }; // class MathUtils
 
 } /* namespace uirenderer */
diff --git a/libs/hwui/utils/VirtualLightRefBase.h b/libs/hwui/utils/VirtualLightRefBase.h
deleted file mode 100644
index b545aab..0000000
--- a/libs/hwui/utils/VirtualLightRefBase.h
+++ /dev/null
@@ -1,34 +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.
- */
-#ifndef VIRTUALLIGHTREFBASE_H
-#define VIRTUALLIGHTREFBASE_H
-
-#include <utils/RefBase.h>
-
-namespace android {
-namespace uirenderer {
-
-// This is a wrapper around LightRefBase that simply enforces a virtual
-// destructor to eliminate the template requirement of LightRefBase
-class VirtualLightRefBase : public LightRefBase<VirtualLightRefBase> {
-public:
-    virtual ~VirtualLightRefBase() {}
-};
-
-} /* namespace uirenderer */
-} /* namespace android */
-
-#endif /* VIRTUALLIGHTREFBASE_H */
diff --git a/media/java/android/media/AudioAttributes.java b/media/java/android/media/AudioAttributes.java
new file mode 100644
index 0000000..bb23a36
--- /dev/null
+++ b/media/java/android/media/AudioAttributes.java
@@ -0,0 +1,444 @@
+/*
+ * 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.media;
+
+import android.annotation.IntDef;
+import android.util.Log;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * A class to encapsulate a collection of attributes describing information about an audio
+ * player or recorder.
+ */
+public final class AudioAttributes {
+    private final static String TAG = "AudioAttributes";
+
+    /**
+     * Content type value to use when the content type is unknown, or other than the ones defined.
+     */
+    public final static int CONTENT_TYPE_UNKNOWN = 0;
+    /**
+     * Content type value to use when the content type is speech.
+     */
+    public final static int CONTENT_TYPE_SPEECH = 1;
+    /**
+     * Content type value to use when the content type is music.
+     */
+    public final static int CONTENT_TYPE_MUSIC = 2;
+    /**
+     * Content type value to use when the content type is a soundtrack, typically accompanying
+     * a movie or TV program.
+     */
+    public final static int CONTENT_TYPE_MOVIE = 3;
+    /**
+     * Content type value to use when the content type is a sound used to accompany a user
+     * action, such as a beep or sound effect expressing a key click, or event, such as the
+     * type of a sound for a bonus being received in a game. These sounds are mostly synthesized
+     * or short Foley sounds.
+     */
+    public final static int CONTENT_TYPE_SONIFICATION = 4;
+
+    /**
+     * Usage value to use when the usage is unknown.
+     */
+    public final static int USAGE_UNKNOWN = 0;
+    /**
+     * Usage value to use when the usage is media, such as music, or movie
+     * soundtracks.
+     */
+    public final static int USAGE_MEDIA = 1;
+    /**
+     * Usage value to use when the usage is voice communications, such as telephony
+     * or VoIP.
+     */
+    public final static int USAGE_VOICE_COMMUNICATION = 2;
+    /**
+     * Usage value to use when the usage is in-call signalling, such as with
+     * a "busy" beep, or DTMF tones.
+     */
+    public final static int USAGE_VOICE_COMMUNICATION_SIGNALLING = 3;
+    /**
+     * Usage value to use when the usage is an alarm (e.g. wake-up alarm).
+     */
+    public final static int USAGE_ALARM = 4;
+    /**
+     * Usage value to use when the usage is notification. See other
+     * notification usages for more specialized uses.
+     */
+    public final static int USAGE_NOTIFICATION = 5;
+    /**
+     * Usage value to use when the usage is telephony ringtone.
+     */
+    public final static int USAGE_NOTIFICATION_TELEPHONY_RINGTONE = 6;
+    /**
+     * Usage value to use when the usage is a request to enter/end a
+     * communication, such as a VoIP communication or video-conference.
+     */
+    public final static int USAGE_NOTIFICATION_COMMUNICATION_REQUEST = 7;
+    /**
+     * Usage value to use when the usage is notification for an "instant"
+     * communication such as a chat, or SMS.
+     */
+    public final static int USAGE_NOTIFICATION_COMMUNICATION_INSTANT = 8;
+    /**
+     * Usage value to use when the usage is notification for a
+     * non-immediate type of communication such as e-mail.
+     */
+    public final static int USAGE_NOTIFICATION_COMMUNICATION_DELAYED = 9;
+    /**
+     * Usage value to use when the usage is to attract the user's attention,
+     * such as a reminder or low battery warning.
+     */
+    public final static int USAGE_NOTIFICATION_EVENT = 10;
+    /**
+     * Usage value to use when the usage is for accessibility, such as with
+     * a screen reader.
+     */
+    public final static int USAGE_ASSISTANCE_ACCESSIBILITY = 11;
+    /**
+     * Usage value to use when the usage is driving or navigation directions.
+     */
+    public final static int USAGE_ASSISTANCE_NAVIGATION_GUIDANCE = 12;
+    /**
+     * Usage value to use when the usage is sonification, such as  with user
+     * interface sounds.
+     */
+    public final static int USAGE_ASSISTANCE_SONIFICATION = 13;
+    /**
+     * Usage value to use when the usage is for game audio.
+     */
+    public final static int USAGE_GAME = 14;
+
+    /**
+     * Flag defining a behavior where the audibility of the sound will be ensured by the system.
+     */
+    public final static int FLAG_AUDIBILITY_ENFORCED = 0x1 << 0;
+    /**
+     * @hide
+     * Flag defining a behavior where the playback of the sound is ensured without
+     * degradation only when going to a secure sink.
+     */
+    // FIXME not guaranteed yet
+    // TODO  add OR to getFlags() when supported and in public API
+    public final static int FLAG_SECURE = 0x1 << 1;
+    /**
+     * @hide
+     * Flag to enable when the stream is associated with SCO usage.
+     * Internal use only for dealing with legacy STREAM_BLUETOOTH_SCO
+     */
+    public final static int FLAG_SCO = 0x1 << 2;
+
+
+    private int mUsage = USAGE_UNKNOWN;
+    private int mContentType = CONTENT_TYPE_UNKNOWN;
+    private int mFlags = 0x0;
+    private HashSet<String> mTags;
+
+    private AudioAttributes() {
+    }
+
+    /**
+     * Return the content type.
+     * @return one of the values that can be set in {@link Builder#setContentType(int)}
+     */
+    public int getContentType() {
+        return mContentType;
+    }
+
+    /**
+     * Return the usage.
+     * @return one of the values that can be set in {@link Builder#setUsage(int)}
+     */
+    public int getUsage() {
+        return mUsage;
+    }
+
+    /**
+     * Return the flags.
+     * @return a combined mask of all flags
+     */
+    public int getFlags() {
+        // only return the flags that are public
+        return (mFlags & (FLAG_AUDIBILITY_ENFORCED));
+    }
+
+    /**
+     * @hide
+     * Return all the flags, even the non-public ones.
+     * Internal use only
+     * @return a combined mask of all flags
+     */
+    public int getAllFlags() {
+        return mFlags;
+    }
+
+    /**
+     * Return the set of tags.
+     * @return a read-only set of all tags stored as strings.
+     */
+    public Set<String> getTags() {
+        return Collections.unmodifiableSet(mTags);
+    }
+
+    /**
+     * Builder class for {@link AudioAttributes} objects.
+     */
+    public static class Builder {
+        private int mUsage = USAGE_UNKNOWN;
+        private int mContentType = CONTENT_TYPE_UNKNOWN;
+        private int mFlags = 0x0;
+        private HashSet<String> mTags = new HashSet<String>();
+
+        /**
+         * Constructs a new Builder with the defaults.
+         */
+        public Builder() {
+        }
+
+        /**
+         * Constructs a new Builder from a given AudioAttributes
+         * @param aa the AudioAttributes object whose data will be reused in the new Builder.
+         */
+        @SuppressWarnings("unchecked") // for cloning of mTags
+        public Builder(AudioAttributes aa) {
+            mUsage = aa.mUsage;
+            mContentType = aa.mContentType;
+            mFlags = aa.mFlags;
+            mTags = (HashSet<String>) aa.mTags.clone();
+        }
+
+        /**
+         * Combines all of the attributes that have been set and return a new
+         * {@link AudioAttributes} object.
+         * @return a new {@link AudioAttributes} object
+         */
+        @SuppressWarnings("unchecked") // for cloning of mTags
+        public AudioAttributes build() {
+            AudioAttributes aa = new AudioAttributes();
+            aa.mContentType = mContentType;
+            aa.mUsage = mUsage;
+            aa.mFlags = mFlags;
+            aa.mTags = (HashSet<String>) mTags.clone();
+            return aa;
+        }
+
+        /**
+         * Sets the attribute describing what is the intended use of the the audio signal,
+         * such as alarm or ringtone.
+         * @param usage one of {@link AudioAttributes#USAGE_UNKNOWN},
+         *     {@link AudioAttributes#USAGE_MEDIA},
+         *     {@link AudioAttributes#USAGE_VOICE_COMMUNICATION},
+         *     {@link AudioAttributes#USAGE_VOICE_COMMUNICATION_SIGNALLING},
+         *     {@link AudioAttributes#USAGE_ALARM}, {@link AudioAttributes#USAGE_NOTIFICATION},
+         *     {@link AudioAttributes#USAGE_NOTIFICATION_TELEPHONY_RINGTONE},
+         *     {@link AudioAttributes#USAGE_NOTIFICATION_COMMUNICATION_REQUEST},
+         *     {@link AudioAttributes#USAGE_NOTIFICATION_COMMUNICATION_INSTANT},
+         *     {@link AudioAttributes#USAGE_NOTIFICATION_COMMUNICATION_DELAYED},
+         *     {@link AudioAttributes#USAGE_NOTIFICATION_EVENT},
+         *     {@link AudioAttributes#USAGE_ASSISTANCE_ACCESSIBILITY},
+         *     {@link AudioAttributes#USAGE_ASSISTANCE_NAVIGATION_GUIDANCE},
+         *     {@link AudioAttributes#USAGE_ASSISTANCE_SONIFICATION},
+         *     {@link AudioAttributes#USAGE_GAME}.
+         * @return the same Builder instance.
+         */
+        public Builder setUsage(@AttributeUsage int usage) {
+            switch (usage) {
+                case USAGE_UNKNOWN:
+                case USAGE_MEDIA:
+                case USAGE_VOICE_COMMUNICATION:
+                case USAGE_VOICE_COMMUNICATION_SIGNALLING:
+                case USAGE_ALARM:
+                case USAGE_NOTIFICATION:
+                case USAGE_NOTIFICATION_TELEPHONY_RINGTONE:
+                case USAGE_NOTIFICATION_COMMUNICATION_REQUEST:
+                case USAGE_NOTIFICATION_COMMUNICATION_INSTANT:
+                case USAGE_NOTIFICATION_COMMUNICATION_DELAYED:
+                case USAGE_NOTIFICATION_EVENT:
+                case USAGE_ASSISTANCE_ACCESSIBILITY:
+                case USAGE_ASSISTANCE_NAVIGATION_GUIDANCE:
+                case USAGE_ASSISTANCE_SONIFICATION:
+                case USAGE_GAME:
+                     mUsage = usage;
+                     break;
+                default:
+                     mUsage = USAGE_UNKNOWN;
+            }
+            return this;
+        }
+
+        /**
+         * Sets the attribute describing the content type of the audio signal, such as speech,
+         * or music.
+         * @param contentType the content type values, one of
+         *     {@link AudioAttributes#CONTENT_TYPE_MOVIE},
+         *     {@link AudioAttributes#CONTENT_TYPE_MUSIC},
+         *     {@link AudioAttributes#CONTENT_TYPE_SONIFICATION},
+         *     {@link AudioAttributes#CONTENT_TYPE_SPEECH},
+         *     {@link AudioAttributes#CONTENT_TYPE_UNKNOWN}.
+         * @return the same Builder instance.
+         */
+        public Builder setContentType(@AttributeContentType int contentType) {
+            switch (contentType) {
+                case CONTENT_TYPE_UNKNOWN:
+                case CONTENT_TYPE_MOVIE:
+                case CONTENT_TYPE_MUSIC:
+                case CONTENT_TYPE_SONIFICATION:
+                case CONTENT_TYPE_SPEECH:
+                     mContentType = contentType;
+                     break;
+                default:
+                     mUsage = CONTENT_TYPE_UNKNOWN;
+            }
+            return this;
+        }
+
+        /**
+         * Sets the combination of flags.
+         * @param flags the {@link AudioAttributes#FLAG_AUDIBILITY_ENFORCED} flag.
+         * @return the same Builder instance.
+         */
+        public Builder setFlags(int flags) {
+            flags &= (AudioAttributes.FLAG_AUDIBILITY_ENFORCED | AudioAttributes.FLAG_SCO
+                    | AudioAttributes.FLAG_SECURE);
+            mFlags |= flags;
+            return this;
+        }
+
+        /**
+         * Add a custom tag stored as a string
+         * @param tag
+         * @return the same Builder instance.
+         */
+        public Builder addTag(String tag) {
+            mTags.add(tag);
+            return this;
+        }
+
+        /**
+         * Adds attributes inferred from the legacy stream types.
+         * @param streamType one of {@link AudioManager#STREAM_VOICE_CALL},
+         *   {@link AudioManager#STREAM_SYSTEM}, {@link AudioManager#STREAM_RING},
+         *   {@link AudioManager#STREAM_MUSIC}, {@link AudioManager#STREAM_ALARM},
+         *    or {@link AudioManager#STREAM_NOTIFICATION}.
+         * @return the same Builder instance.
+         */
+        public Builder setLegacyStreamType(int streamType) {
+            return setInternalLegacyStreamType(streamType);
+        }
+
+        /**
+         * @hide
+         * For internal framework use only, enables building from hidden stream types.
+         * @param streamType
+         * @return the same Builder instance.
+         */
+        public Builder setInternalLegacyStreamType(int streamType) {
+            switch(streamType) {
+                case AudioSystem.STREAM_VOICE_CALL:
+                    mContentType = CONTENT_TYPE_SPEECH;
+                    mUsage = USAGE_VOICE_COMMUNICATION;
+                    break;
+                case AudioSystem.STREAM_SYSTEM_ENFORCED:
+                    mFlags |= FLAG_AUDIBILITY_ENFORCED;
+                    // intended fall through, attributes in common with STREAM_SYSTEM
+                case AudioSystem.STREAM_SYSTEM:
+                    mContentType = CONTENT_TYPE_SONIFICATION;
+                    mUsage = USAGE_ASSISTANCE_SONIFICATION;
+                    break;
+                case AudioSystem.STREAM_RING:
+                    mContentType = CONTENT_TYPE_SONIFICATION;
+                    mUsage = USAGE_NOTIFICATION_TELEPHONY_RINGTONE;
+                    break;
+                case AudioSystem.STREAM_MUSIC:
+                    mContentType = CONTENT_TYPE_MUSIC;
+                    mUsage = USAGE_MEDIA;
+                    break;
+                case AudioSystem.STREAM_ALARM:
+                    mContentType = CONTENT_TYPE_SONIFICATION;
+                    mUsage = USAGE_ALARM;
+                    break;
+                case AudioSystem.STREAM_NOTIFICATION:
+                    mContentType = CONTENT_TYPE_SONIFICATION;
+                    mUsage = USAGE_NOTIFICATION;
+                    break;
+                case AudioSystem.STREAM_BLUETOOTH_SCO:
+                    mContentType = CONTENT_TYPE_SPEECH;
+                    mUsage = USAGE_VOICE_COMMUNICATION;
+                    mFlags |= FLAG_SCO;
+                    break;
+                case AudioSystem.STREAM_DTMF:
+                    mContentType = CONTENT_TYPE_SONIFICATION;
+                    mUsage = USAGE_VOICE_COMMUNICATION_SIGNALLING;
+                    break;
+                case AudioSystem.STREAM_TTS:
+                    mContentType = CONTENT_TYPE_SPEECH;
+                    mUsage = USAGE_ASSISTANCE_ACCESSIBILITY;
+                    break;
+                default:
+                    Log.e(TAG, "Invalid stream type " + streamType + " in for AudioAttributes");
+            }
+            return this;
+        }
+    };
+
+    /** @hide */
+    @Override
+    public String toString () {
+        return new String("AudioAttributes:"
+                + " usage=" + mUsage
+                + " content=" + mContentType
+                + " flags=0x" + Integer.toHexString(mFlags)
+                + " tags=" + mTags);
+    }
+
+    /** @hide */
+    @IntDef({
+        USAGE_UNKNOWN,
+        USAGE_MEDIA,
+        USAGE_VOICE_COMMUNICATION,
+        USAGE_VOICE_COMMUNICATION_SIGNALLING,
+        USAGE_ALARM,
+        USAGE_NOTIFICATION,
+        USAGE_NOTIFICATION_TELEPHONY_RINGTONE,
+        USAGE_NOTIFICATION_COMMUNICATION_REQUEST,
+        USAGE_NOTIFICATION_COMMUNICATION_INSTANT,
+        USAGE_NOTIFICATION_COMMUNICATION_DELAYED,
+        USAGE_NOTIFICATION_EVENT,
+        USAGE_ASSISTANCE_ACCESSIBILITY,
+        USAGE_ASSISTANCE_NAVIGATION_GUIDANCE,
+        USAGE_ASSISTANCE_SONIFICATION,
+        USAGE_GAME
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface AttributeUsage {}
+
+    /** @hide */
+    @IntDef({
+        CONTENT_TYPE_UNKNOWN,
+        CONTENT_TYPE_SPEECH,
+        CONTENT_TYPE_MUSIC,
+        CONTENT_TYPE_MOVIE,
+        CONTENT_TYPE_SONIFICATION
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface AttributeContentType {}
+}
diff --git a/media/java/android/media/AudioFormat.java b/media/java/android/media/AudioFormat.java
index 6b2a247..57274ee 100644
--- a/media/java/android/media/AudioFormat.java
+++ b/media/java/android/media/AudioFormat.java
@@ -37,7 +37,7 @@
     public static final int ENCODING_PCM_16BIT = 2;
     /** Audio data format: PCM 8 bit per sample. Not guaranteed to be supported by devices. */
     public static final int ENCODING_PCM_8BIT = 3;
-    /** @hide Candidate for public API */
+    /** Audio data format: single-precision floating-point per sample */
     public static final int ENCODING_PCM_FLOAT = 4;
 
     /** Invalid audio channel configuration */
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index dab6eed..1a64cff 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -42,7 +42,8 @@
  * The AudioTrack class manages and plays a single audio resource for Java applications.
  * It allows streaming of PCM audio buffers to the audio sink for playback. This is
  * achieved by "pushing" the data to the AudioTrack object using one of the
- *  {@link #write(byte[], int, int)} and {@link #write(short[], int, int)} methods.
+ *  {@link #write(byte[], int, int)}, {@link #write(short[], int, int)},
+ *  and {@link #write(float[], int, int, int)} methods.
  *
  * <p>An AudioTrack instance can operate under two modes: static or streaming.<br>
  * In Streaming mode, the application writes a continuous stream of data to the AudioTrack, using
@@ -72,7 +73,6 @@
  *
  * AudioTrack is not final and thus permits subclasses, but such use is not recommended.
  */
-// add {@link #write(float[], int, int)} when @hide removed
 public class AudioTrack
 {
     //---------------------------------------------------------
@@ -245,8 +245,8 @@
      * The encoding of the audio samples.
      * @see AudioFormat#ENCODING_PCM_8BIT
      * @see AudioFormat#ENCODING_PCM_16BIT
+     * @see AudioFormat#ENCODING_PCM_FLOAT
      */
-    // add @see AudioFormat#ENCODING_PCM_FLOAT when @hide removed
     private int mAudioFormat = AudioFormat.ENCODING_PCM_16BIT;
     /**
      * Audio session ID
@@ -287,8 +287,9 @@
      *   See {@link AudioFormat#CHANNEL_OUT_MONO} and
      *   {@link AudioFormat#CHANNEL_OUT_STEREO}
      * @param audioFormat the format in which the audio data is represented.
-     *   See {@link AudioFormat#ENCODING_PCM_16BIT} and
-     *   {@link AudioFormat#ENCODING_PCM_8BIT}
+     *   See {@link AudioFormat#ENCODING_PCM_16BIT},
+     *   {@link AudioFormat#ENCODING_PCM_8BIT},
+     *   and {@link AudioFormat#ENCODING_PCM_FLOAT}.
      * @param bufferSizeInBytes the total size (in bytes) of the internal buffer where audio data is
      *   read from for playback.
      *   If track's creation mode is {@link #MODE_STREAM}, you can write data into
@@ -302,7 +303,6 @@
      * @param mode streaming or static buffer. See {@link #MODE_STATIC} and {@link #MODE_STREAM}
      * @throws java.lang.IllegalArgumentException
      */
-    // add {@link AudioFormat#ENCODING_PCM_FLOAT} to audioFormat section above, when @hide removed
     public AudioTrack(int streamType, int sampleRateInHz, int channelConfig, int audioFormat,
             int bufferSizeInBytes, int mode)
     throws IllegalArgumentException {
@@ -332,7 +332,8 @@
      *   {@link AudioFormat#CHANNEL_OUT_STEREO}
      * @param audioFormat the format in which the audio data is represented.
      *   See {@link AudioFormat#ENCODING_PCM_16BIT} and
-     *   {@link AudioFormat#ENCODING_PCM_8BIT}
+     *   {@link AudioFormat#ENCODING_PCM_8BIT},
+     *   and {@link AudioFormat#ENCODING_PCM_FLOAT}.
      * @param bufferSizeInBytes the total size (in bytes) of the buffer where audio data is read
      *   from for playback. If using the AudioTrack in streaming mode, you can write data into
      *   this buffer in smaller chunks than this size. If using the AudioTrack in static mode,
@@ -344,7 +345,6 @@
      * @param sessionId Id of audio session the AudioTrack must be attached to
      * @throws java.lang.IllegalArgumentException
      */
-    // add {@link AudioFormat#ENCODING_PCM_FLOAT} to audioFormat section above, when @hide removed
     public AudioTrack(int streamType, int sampleRateInHz, int channelConfig, int audioFormat,
             int bufferSizeInBytes, int mode, int sessionId)
     throws IllegalArgumentException {
@@ -469,7 +469,7 @@
         default:
             throw new IllegalArgumentException("Unsupported sample encoding."
                 + " Should be ENCODING_PCM_8BIT or ENCODING_PCM_16BIT"
-             // + " or ENCODING_PCM_FLOAT" when @hide removed
+                + " or ENCODING_PCM_FLOAT"
                 + ".");
         }
 
@@ -730,12 +730,12 @@
      *   {@link AudioFormat#CHANNEL_OUT_STEREO}
      * @param audioFormat the format in which the audio data is represented.
      *   See {@link AudioFormat#ENCODING_PCM_16BIT} and
-     *   {@link AudioFormat#ENCODING_PCM_8BIT}
+     *   {@link AudioFormat#ENCODING_PCM_8BIT},
+     *   and {@link AudioFormat#ENCODING_PCM_FLOAT}.
      * @return {@link #ERROR_BAD_VALUE} if an invalid parameter was passed,
      *   or {@link #ERROR} if unable to query for output properties,
      *   or the minimum buffer size expressed in bytes.
      */
-    // add {@link AudioFormat#ENCODING_PCM_FLOAT} to audioFormat section above, when @hide removed
     static public int getMinBufferSize(int sampleRateInHz, int channelConfig, int audioFormat) {
         int channelCount = 0;
         switch(channelConfig) {
@@ -1259,7 +1259,6 @@
      * @return the number of floats that were written, or {@link #ERROR_INVALID_OPERATION}
      *    if the object wasn't properly initialized, or {@link #ERROR_BAD_VALUE} if
      *    the parameters don't resolve to valid data and indexes.
-     * @hide candidate for public API
      */
     public int write(float[] audioData, int offsetInFloats, int sizeInFloats,
             @WriteMode int writeMode) {
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index 115786c..34c55202 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -585,11 +585,63 @@
      * the codec. If you previously specified a surface when configuring this
      * video decoder you can optionally render the buffer.
      * @param index The index of a client-owned output buffer previously returned
-     *              in a call to {@link #dequeueOutputBuffer}.
+     *              from a call to {@link #dequeueOutputBuffer}.
      * @param render If a valid surface was specified when configuring the codec,
      *               passing true renders this output buffer to the surface.
      */
-    public native final void releaseOutputBuffer(int index, boolean render);
+    public final void releaseOutputBuffer(int index, boolean render) {
+        releaseOutputBuffer(index, render, false /* updatePTS */, 0 /* dummy */);
+    }
+
+    /**
+     * If you are done with a buffer, use this call to update its surface timestamp
+     * and return it to the codec to render it on the output surface. If you
+     * have not specified an output surface when configuring this video codec,
+     * this call will simply return the buffer to the codec.<p>
+     *
+     * The timestamp may have special meaning depending on the destination surface.
+     *
+     * <table>
+     * <tr><th>SurfaceView specifics</th></tr>
+     * <tr><td>
+     * If you render your buffer on a {@link android.view.SurfaceView},
+     * you can use the timestamp to render the buffer at a specific time (at the
+     * VSYNC at or after the buffer timestamp).  For this to work, the timestamp
+     * needs to be <i>reasonably close</i> to the current {@link System#nanoTime}.
+     * Currently, this is set as within one (1) second. A few notes:
+     *
+     * <ul>
+     * <li>the buffer will not be returned to the codec until the timestamp
+     * has passed and the buffer is no longer used by the {@link android.view.Surface}.
+     * <li>buffers are processed sequentially, so you may block subsequent buffers to
+     * be displayed on the {@link android.view.Surface}.  This is important if you
+     * want to react to user action, e.g. stop the video or seek.
+     * <li>if multiple buffers are sent to the {@link android.view.Surface} to be
+     * rendered at the same VSYNC, the last one will be shown, and the other ones
+     * will be dropped.
+     * <li>if the timestamp is <em>not</em> "reasonably close" to the current system
+     * time, the {@link android.view.Surface} will ignore the timestamp, and
+     * display the buffer at the earliest feasible time.  In this mode it will not
+     * drop frames.
+     * <li>for best performance and quality, call this method when you are about
+     * two VSYNCs' time before the desired render time.  For 60Hz displays, this is
+     * about 33 msec.
+     * </ul>
+     * </td></tr>
+     * </table>
+     *
+     * @param index The index of a client-owned output buffer previously returned
+     *              from a call to {@link #dequeueOutputBuffer}.
+     * @param renderTimestampNs The timestamp to associate with this buffer when
+     *              it is sent to the Surface.
+     */
+    public final void releaseOutputBuffer(int index, long renderTimestampNs) {
+        releaseOutputBuffer(
+                index, true /* render */, true /* updatePTS */, renderTimestampNs);
+    }
+
+    private native final void releaseOutputBuffer(
+            int index, boolean render, boolean updatePTS, long timeNs);
 
     /**
      * Signals end-of-stream on input.  Equivalent to submitting an empty buffer with
diff --git a/media/java/android/media/session/ISessionManager.aidl b/media/java/android/media/session/ISessionManager.aidl
index 7a8c22e..e341647 100644
--- a/media/java/android/media/session/ISessionManager.aidl
+++ b/media/java/android/media/session/ISessionManager.aidl
@@ -25,6 +25,6 @@
  * @hide
  */
 interface ISessionManager {
-    ISession createSession(String packageName, in ISessionCallback cb, String tag);
-    List<IBinder> getSessions(in ComponentName compName);
+    ISession createSession(String packageName, in ISessionCallback cb, String tag, int userId);
+    List<IBinder> getSessions(in ComponentName compName, int userId);
 }
\ No newline at end of file
diff --git a/media/java/android/media/session/SessionManager.java b/media/java/android/media/session/SessionManager.java
index fd022fc..1eb3b7a 100644
--- a/media/java/android/media/session/SessionManager.java
+++ b/media/java/android/media/session/SessionManager.java
@@ -22,6 +22,7 @@
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.os.UserHandle;
 import android.service.notification.NotificationListenerService;
 import android.util.Log;
 
@@ -65,10 +66,25 @@
      * @return a {@link Session} for the new session
      */
     public Session createSession(String tag) {
+        return createSessionAsUser(tag, UserHandle.myUserId());
+    }
+
+    /**
+     * Creates a new session as the specified user. To create a session as a
+     * user other than your own you must hold the
+     * {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL}
+     * permission.
+     *
+     * @param tag A short name for debugging purposes
+     * @param userId The user id to create the session as.
+     * @return a {@link Session} for the new session
+     * @hide
+     */
+    public Session createSessionAsUser(String tag, int userId) {
         try {
             Session.CallbackStub cbStub = new Session.CallbackStub();
             Session session = new Session(mService
-                    .createSession(mContext.getPackageName(), cbStub, tag), cbStub);
+                    .createSession(mContext.getPackageName(), cbStub, tag, userId), cbStub);
             cbStub.setMediaSession(session);
 
             return session;
@@ -91,9 +107,27 @@
      * @return A list of controllers for ongoing sessions
      */
     public List<SessionController> getActiveSessions(ComponentName notificationListener) {
+        return getActiveSessionsForUser(notificationListener, UserHandle.myUserId());
+    }
+
+    /**
+     * Get active sessions for a specific user. To retrieve actions for a user
+     * other than your own you must hold the
+     * {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL} permission
+     * in addition to any other requirements. If you are an enabled notification
+     * listener you may only get sessions for the users you are enabled for.
+     *
+     * @param notificationListener The enabled notification listener component.
+     *            May be null.
+     * @param userId The user id to fetch sessions for.
+     * @return A list of controllers for ongoing sessions.
+     * @hide
+     */
+    public List<SessionController> getActiveSessionsForUser(ComponentName notificationListener,
+            int userId) {
         ArrayList<SessionController> controllers = new ArrayList<SessionController>();
         try {
-            List<IBinder> binders = mService.getSessions(notificationListener);
+            List<IBinder> binders = mService.getSessions(notificationListener, userId);
             for (int i = binders.size() - 1; i >= 0; i--) {
                 SessionController controller = SessionController.fromBinder(ISessionController.Stub
                         .asInterface(binders.get(i)));
diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp
index a710c03..4a7c096 100644
--- a/media/jni/android_media_MediaCodec.cpp
+++ b/media/jni/android_media_MediaCodec.cpp
@@ -260,7 +260,11 @@
     return OK;
 }
 
-status_t JMediaCodec::releaseOutputBuffer(size_t index, bool render) {
+status_t JMediaCodec::releaseOutputBuffer(
+        size_t index, bool render, bool updatePTS, int64_t timestampNs) {
+    if (updatePTS) {
+        return mCodec->renderOutputBufferAndRelease(index, timestampNs);
+    }
     return render
         ? mCodec->renderOutputBufferAndRelease(index)
         : mCodec->releaseOutputBuffer(index);
@@ -873,7 +877,8 @@
 }
 
 static void android_media_MediaCodec_releaseOutputBuffer(
-        JNIEnv *env, jobject thiz, jint index, jboolean render) {
+        JNIEnv *env, jobject thiz,
+        jint index, jboolean render, jboolean updatePTS, jlong timestampNs) {
     ALOGV("android_media_MediaCodec_renderOutputBufferAndRelease");
 
     sp<JMediaCodec> codec = getMediaCodec(env, thiz);
@@ -883,7 +888,7 @@
         return;
     }
 
-    status_t err = codec->releaseOutputBuffer(index, render);
+    status_t err = codec->releaseOutputBuffer(index, render, updatePTS, timestampNs);
 
     throwExceptionAsNecessary(env, err);
 }
@@ -1138,7 +1143,7 @@
     { "dequeueOutputBuffer", "(Landroid/media/MediaCodec$BufferInfo;J)I",
       (void *)android_media_MediaCodec_dequeueOutputBuffer },
 
-    { "releaseOutputBuffer", "(IZ)V",
+    { "releaseOutputBuffer", "(IZZJ)V",
       (void *)android_media_MediaCodec_releaseOutputBuffer },
 
     { "signalEndOfInputStream", "()V",
diff --git a/media/jni/android_media_MediaCodec.h b/media/jni/android_media_MediaCodec.h
index 2f2ea96..bf9f4ea 100644
--- a/media/jni/android_media_MediaCodec.h
+++ b/media/jni/android_media_MediaCodec.h
@@ -79,7 +79,8 @@
     status_t dequeueOutputBuffer(
             JNIEnv *env, jobject bufferInfo, size_t *index, int64_t timeoutUs);
 
-    status_t releaseOutputBuffer(size_t index, bool render);
+    status_t releaseOutputBuffer(
+            size_t index, bool render, bool updatePTS, int64_t timestampNs);
 
     status_t signalEndOfInputStream();
 
diff --git a/packages/DocumentsUI/res/values-km-rKH/strings.xml b/packages/DocumentsUI/res/values-km-rKH/strings.xml
index e8944ec..8c9030d 100644
--- a/packages/DocumentsUI/res/values-km-rKH/strings.xml
+++ b/packages/DocumentsUI/res/values-km-rKH/strings.xml
@@ -20,14 +20,14 @@
     <string name="title_open" msgid="4353228937663917801">"បើក​ពី"</string>
     <string name="title_save" msgid="2433679664882857999">"រក្សា​ទុក​ទៅ"</string>
     <string name="menu_create_dir" msgid="5947289605844398389">"បង្កើត​ថត"</string>
-    <string name="menu_grid" msgid="6878021334497835259">"ទិដ្ឋភាព​ក្រឡា"</string>
+    <string name="menu_grid" msgid="6878021334497835259">"ទិដ្ឋភាព​ក្រឡា​"</string>
     <string name="menu_list" msgid="7279285939892417279">"ទិដ្ឋភាព​បញ្ជី"</string>
     <string name="menu_sort" msgid="7677740407158414452">"តម្រៀប​តាម"</string>
     <string name="menu_search" msgid="3816712084502856974">"ស្វែងរក"</string>
     <string name="menu_settings" msgid="6008033148948428823">"ការ​កំណត់"</string>
     <string name="menu_open" msgid="432922957274920903">"បើក"</string>
     <string name="menu_save" msgid="2394743337684426338">"រក្សាទុក"</string>
-    <string name="menu_share" msgid="3075149983979628146">"ចែករំលែក"</string>
+    <string name="menu_share" msgid="3075149983979628146">"ចែករំលែក​"</string>
     <string name="menu_delete" msgid="8138799623850614177">"លុប"</string>
     <string name="menu_select" msgid="8711270657353563424">"ជ្រើស \"<xliff:g id="DIRECTORY">^1</xliff:g>\""</string>
     <string name="mode_selected_count" msgid="459111894725594625">"បាន​ជ្រើស <xliff:g id="COUNT">%1$d</xliff:g>"</string>
@@ -48,7 +48,7 @@
     <string name="pref_advanced_devices" msgid="903257239609301276">"បង្ហាញ​ឧបករណ៍​កម្រិត​ខ្ពស់"</string>
     <string name="pref_file_size" msgid="2826879315743961459">"បង្ហាញ​ទំហំ​ឯកសារ"</string>
     <string name="pref_device_size" msgid="3542106883278997222">"បង្ហាញ​ទំហំ​ឧបករណ៍"</string>
-    <string name="empty" msgid="7858882803708117596">"គ្មានធាតុ"</string>
+    <string name="empty" msgid="7858882803708117596">"គ្មានធាតុ​"</string>
     <string name="toast_no_application" msgid="1339885974067891667">"មិន​អាច​បើក​ឯកសារ"</string>
     <string name="toast_failed_delete" msgid="2180678019407244069">"មិន​អាច​លុប​ឯកសារ​មួយ​ចំនួន"</string>
     <string name="share_via" msgid="8966594246261344259">"ចែករំលែក​តាម"</string>
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
index b2b2bd8..9069a55 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java
@@ -436,6 +436,8 @@
         @Override
         public boolean onCreateActionMode(ActionMode mode, Menu menu) {
             mode.getMenuInflater().inflate(R.menu.mode_directory, menu);
+            mode.setTitle(getResources()
+                    .getString(R.string.mode_selected_count, mCurrentView.getCheckedItemCount()));
             return true;
         }
 
diff --git a/packages/Keyguard/res/layout/keyguard_status_view.xml b/packages/Keyguard/res/layout/keyguard_status_view.xml
index 546ddd4..0d943ed 100644
--- a/packages/Keyguard/res/layout/keyguard_status_view.xml
+++ b/packages/Keyguard/res/layout/keyguard_status_view.xml
@@ -28,7 +28,7 @@
     androidprv:layout_maxWidth="@dimen/keyguard_security_width"
     androidprv:layout_maxHeight="@dimen/keyguard_security_height"
     android:gravity="center_horizontal|top"
-    android:layout_marginTop="32dp"
+    android:layout_marginTop="48dp"
     android:layout_marginBottom="32dp"
     android:contentDescription="@string/keyguard_accessibility_status">
     <LinearLayout
diff --git a/packages/Keyguard/res/values-km-rKH/strings.xml b/packages/Keyguard/res/values-km-rKH/strings.xml
index a2e54a7..18b59f1 100644
--- a/packages/Keyguard/res/values-km-rKH/strings.xml
+++ b/packages/Keyguard/res/values-km-rKH/strings.xml
@@ -83,7 +83,7 @@
     <string name="password_keyboard_label_alpha_key" msgid="8001096175167485649">"ABC"</string>
     <string name="password_keyboard_label_alt_key" msgid="1284820942620288678">"ALT"</string>
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
-    <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"បោះ​បង់"</string>
+    <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"បោះ​បង់​"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"លុប"</string>
     <string name="keyboardview_keycode_done" msgid="1992571118466679775">"រួចរាល់"</string>
     <string name="keyboardview_keycode_mode_change" msgid="4547387741906537519">"ប្ដូរ​របៀប"</string>
@@ -120,7 +120,7 @@
     <string name="kg_login_too_many_attempts" msgid="6486842094005698475">"ព្យាយាម​លំនាំ​ច្រើន​ពេក"</string>
     <string name="kg_login_instructions" msgid="1100551261265506448">"ដើម្បី​ដោះ​សោ ចូល​ក្នុង​គណនី Google ។"</string>
     <string name="kg_login_username_hint" msgid="5718534272070920364">"ឈ្មោះ​អ្នក​ប្រើ (អ៊ី​ម៉ែ​ល​)"</string>
-    <string name="kg_login_password_hint" msgid="9057289103827298549">"ពាក្យសម្ងាត់"</string>
+    <string name="kg_login_password_hint" msgid="9057289103827298549">"ពាក្យសម្ងាត់​"</string>
     <string name="kg_login_submit_button" msgid="5355904582674054702">"ចូល"</string>
     <string name="kg_login_invalid_input" msgid="5754664119319872197">"ឈ្មោះ​អ្នកប្រើ ឬ​ពាក្យ​សម្ងាត់​មិន​ត្រឹមត្រូវ។"</string>
     <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"ភ្លេច​ឈ្មោះ​អ្នកប្រើ ឬ​ពាក្យ​សម្ងាត់​របស់​អ្នក?\nមើល "<b>"google.com/accounts/recovery"</b>" ។"</string>
diff --git a/packages/PrintSpooler/res/values-km-rKH/strings.xml b/packages/PrintSpooler/res/values-km-rKH/strings.xml
index c89f9bf..ba3c042 100644
--- a/packages/PrintSpooler/res/values-km-rKH/strings.xml
+++ b/packages/PrintSpooler/res/values-km-rKH/strings.xml
@@ -60,7 +60,7 @@
   </plurals>
     <string name="cancel" msgid="4373674107267141885">"បោះបង់"</string>
     <string name="restart" msgid="2472034227037808749">"ចាប់ផ្ដើម​ឡើងវិញ"</string>
-    <string name="no_connection_to_printer" msgid="2159246915977282728">"គ្មាន​​​ការ​ភ្ជាប់​ទៅ​ម៉ាស៊ីន​បោះពុម្ព"</string>
+    <string name="no_connection_to_printer" msgid="2159246915977282728">"គ្មាន​​​ការ​ភ្ជាប់​ទៅ​ម៉ាស៊ីន​បោះពុម្ព​"</string>
     <string name="reason_unknown" msgid="5507940196503246139">"មិន​ស្គាល់"</string>
     <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – មិន​អាច​ប្រើ​បាន"</string>
     <string name="print_error_default_message" msgid="8568506918983980567">"មិន​អាច​បង្កើត​ការ​ងារ​បោះពុម្ព"</string>
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_notify_clear_normal.png b/packages/SystemUI/res/drawable-hdpi/ic_notify_clear_normal.png
deleted file mode 100644
index 54dde82..0000000
--- a/packages/SystemUI/res/drawable-hdpi/ic_notify_clear_normal.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_notify_open_normal.png b/packages/SystemUI/res/drawable-hdpi/ic_notify_open_normal.png
deleted file mode 100644
index 3c0dc4e..0000000
--- a/packages/SystemUI/res/drawable-hdpi/ic_notify_open_normal.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_notify_quicksettings_normal.png b/packages/SystemUI/res/drawable-hdpi/ic_notify_quicksettings_normal.png
deleted file mode 100644
index 3b1944d..0000000
--- a/packages/SystemUI/res/drawable-hdpi/ic_notify_quicksettings_normal.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_notify_settings_normal.png b/packages/SystemUI/res/drawable-hdpi/ic_notify_settings_normal.png
deleted file mode 100644
index 693abf5..0000000
--- a/packages/SystemUI/res/drawable-hdpi/ic_notify_settings_normal.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_qs_contrast_alpha.png b/packages/SystemUI/res/drawable-hdpi/ic_qs_contrast_alpha.png
deleted file mode 100644
index 0f9dfc7..0000000
--- a/packages/SystemUI/res/drawable-hdpi/ic_qs_contrast_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-ldrtl-hdpi/ic_notify_clear_normal.png b/packages/SystemUI/res/drawable-ldrtl-hdpi/ic_notify_clear_normal.png
deleted file mode 100644
index c526433..0000000
--- a/packages/SystemUI/res/drawable-ldrtl-hdpi/ic_notify_clear_normal.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-ldrtl-mdpi/ic_notify_clear_normal.png b/packages/SystemUI/res/drawable-ldrtl-mdpi/ic_notify_clear_normal.png
deleted file mode 100644
index d13bc69..0000000
--- a/packages/SystemUI/res/drawable-ldrtl-mdpi/ic_notify_clear_normal.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-ldrtl-xhdpi/ic_notify_clear_normal.png b/packages/SystemUI/res/drawable-ldrtl-xhdpi/ic_notify_clear_normal.png
deleted file mode 100644
index a137a80..0000000
--- a/packages/SystemUI/res/drawable-ldrtl-xhdpi/ic_notify_clear_normal.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-ldrtl-xxhdpi/ic_notify_clear_normal.png b/packages/SystemUI/res/drawable-ldrtl-xxhdpi/ic_notify_clear_normal.png
deleted file mode 100644
index 8da7945..0000000
--- a/packages/SystemUI/res/drawable-ldrtl-xxhdpi/ic_notify_clear_normal.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_notify_clear_normal.png b/packages/SystemUI/res/drawable-mdpi/ic_notify_clear_normal.png
deleted file mode 100644
index 7cb52e3..0000000
--- a/packages/SystemUI/res/drawable-mdpi/ic_notify_clear_normal.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_notify_open_normal.png b/packages/SystemUI/res/drawable-mdpi/ic_notify_open_normal.png
deleted file mode 100644
index 8010ce7..0000000
--- a/packages/SystemUI/res/drawable-mdpi/ic_notify_open_normal.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_notify_quicksettings_normal.png b/packages/SystemUI/res/drawable-mdpi/ic_notify_quicksettings_normal.png
deleted file mode 100644
index 807f607..0000000
--- a/packages/SystemUI/res/drawable-mdpi/ic_notify_quicksettings_normal.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_notify_settings_normal.png b/packages/SystemUI/res/drawable-mdpi/ic_notify_settings_normal.png
deleted file mode 100644
index 15340d3..0000000
--- a/packages/SystemUI/res/drawable-mdpi/ic_notify_settings_normal.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_qs_contrast_alpha.png b/packages/SystemUI/res/drawable-mdpi/ic_qs_contrast_alpha.png
deleted file mode 100644
index a4dd087..0000000
--- a/packages/SystemUI/res/drawable-mdpi/ic_qs_contrast_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_notify_clear_normal.png b/packages/SystemUI/res/drawable-xhdpi/ic_notify_clear_normal.png
deleted file mode 100644
index b9afa44..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/ic_notify_clear_normal.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_notify_open_normal.png b/packages/SystemUI/res/drawable-xhdpi/ic_notify_open_normal.png
deleted file mode 100644
index 6d46fdd..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/ic_notify_open_normal.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_notify_quicksettings_normal.png b/packages/SystemUI/res/drawable-xhdpi/ic_notify_quicksettings_normal.png
deleted file mode 100644
index e562bc2..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/ic_notify_quicksettings_normal.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_notify_settings_normal.png b/packages/SystemUI/res/drawable-xhdpi/ic_notify_settings_normal.png
deleted file mode 100644
index e3cc9b0..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/ic_notify_settings_normal.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_qs_contrast_alpha.png b/packages/SystemUI/res/drawable-xhdpi/ic_qs_contrast_alpha.png
deleted file mode 100644
index 9331e52..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/ic_qs_contrast_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_notify_clear_normal.png b/packages/SystemUI/res/drawable-xxhdpi/ic_notify_clear_normal.png
deleted file mode 100644
index afdee8f..0000000
--- a/packages/SystemUI/res/drawable-xxhdpi/ic_notify_clear_normal.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_notify_open_normal.png b/packages/SystemUI/res/drawable-xxhdpi/ic_notify_open_normal.png
deleted file mode 100644
index 7742207..0000000
--- a/packages/SystemUI/res/drawable-xxhdpi/ic_notify_open_normal.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_notify_quicksettings_normal.png b/packages/SystemUI/res/drawable-xxhdpi/ic_notify_quicksettings_normal.png
deleted file mode 100644
index a2e8fe1..0000000
--- a/packages/SystemUI/res/drawable-xxhdpi/ic_notify_quicksettings_normal.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_notify_settings_normal.png b/packages/SystemUI/res/drawable-xxhdpi/ic_notify_settings_normal.png
deleted file mode 100644
index e15981a..0000000
--- a/packages/SystemUI/res/drawable-xxhdpi/ic_notify_settings_normal.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_qs_contrast_alpha.png b/packages/SystemUI/res/drawable-xxhdpi/ic_qs_contrast_alpha.png
deleted file mode 100644
index 82c3842..0000000
--- a/packages/SystemUI/res/drawable-xxhdpi/ic_qs_contrast_alpha.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable/ic_notifications.xml b/packages/SystemUI/res/drawable/ic_notifications.xml
deleted file mode 100644
index 97a7623..0000000
--- a/packages/SystemUI/res/drawable/ic_notifications.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2012 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.
--->
-
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:state_pressed="true"
-         android:drawable="@drawable/ic_notify_open_normal" />
-    <item
-         android:drawable="@drawable/ic_notify_open_normal" />
-</selector>
-
diff --git a/packages/SystemUI/res/drawable/ic_notify_clear.xml b/packages/SystemUI/res/drawable/ic_notify_clear.xml
deleted file mode 100644
index 2163198..0000000
--- a/packages/SystemUI/res/drawable/ic_notify_clear.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2011 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.
--->
-
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:state_pressed="true"
-        android:drawable="@drawable/ic_notify_clear_normal" />
-    <item android:drawable="@drawable/ic_notify_clear_normal" />
-</selector>
diff --git a/packages/SystemUI/res/drawable/ic_notify_quicksettings.xml b/packages/SystemUI/res/drawable/ic_notify_quicksettings.xml
deleted file mode 100644
index 7cf3175..0000000
--- a/packages/SystemUI/res/drawable/ic_notify_quicksettings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2012 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.
--->
-
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:state_pressed="true"
-         android:drawable="@drawable/ic_notify_quicksettings_normal" />
-    <item
-         android:drawable="@drawable/ic_notify_quicksettings_normal" />
-</selector>
-
diff --git a/packages/SystemUI/res/drawable/ic_notify_settings.xml b/packages/SystemUI/res/drawable/ic_notify_settings.xml
deleted file mode 100644
index 9303ca4..0000000
--- a/packages/SystemUI/res/drawable/ic_notify_settings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2012 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.
--->
-
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:state_pressed="true"
-         android:drawable="@drawable/ic_notify_settings_normal" />
-    <item
-         android:drawable="@drawable/ic_notify_settings_normal" />
-</selector>
-
diff --git a/packages/SystemUI/res/drawable/ic_qs_contrast_off.xml b/packages/SystemUI/res/drawable/ic_qs_contrast_off.xml
deleted file mode 100644
index 5f65d8a..0000000
--- a/packages/SystemUI/res/drawable/ic_qs_contrast_off.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-     Copyright (C) 2014 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
-    android:src="@drawable/ic_qs_contrast_alpha"
-    android:tint="@color/ic_qs_off" />
diff --git a/packages/SystemUI/res/drawable/ic_settings_24dp.xml b/packages/SystemUI/res/drawable/ic_settings_24dp.xml
new file mode 100644
index 0000000..a2f7822
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_settings_24dp.xml
@@ -0,0 +1,29 @@
+<!-- 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android" >
+<size
+android:width="24dp"
+android:height="24dp"/>
+
+    <viewport android:viewportWidth="24.0"
+          android:viewportHeight="24.0"/>
+
+
+<path
+     android:pathData="M19.4,13.0c0.0,-0.3 0.1,-0.6 0.1,-1.0s0.0,-0.7 -0.1,-1.0l2.1,-1.7c0.2,-0.2 0.2,-0.4 0.1,-0.6l-2.0,-3.5C19.5,5.1 19.3,5.0 19.0,5.1l-2.5,1.0c-0.5,-0.4 -1.1,-0.7 -1.7,-1.0l-0.4,-2.6C14.5,2.2 14.2,2.0 14.0,2.0l-4.0,0.0C9.8,2.0 9.5,2.2 9.5,2.4L9.1,5.1C8.5,5.3 8.0,5.7 7.4,6.1L5.0,5.1C4.7,5.0 4.5,5.1 4.3,5.3l-2.0,3.5C2.2,8.9 2.3,9.2 2.5,9.4L4.6,11.0c0.0,0.3 -0.1,0.6 -0.1,1.0s0.0,0.7 0.1,1.0l-2.1,1.7c-0.2,0.2 -0.2,0.4 -0.1,0.6l2.0,3.5C4.5,18.9 4.7,19.0 5.0,18.9l2.5,-1.0c0.5,0.4 1.1,0.7 1.7,1.0l0.4,2.6c0.0,0.2 0.2,0.4 0.5,0.4l4.0,0.0c0.2,0.0 0.5,-0.2 0.5,-0.4l0.4,-2.6c0.6,-0.3 1.2,-0.6 1.7,-1.0l2.5,1.0c0.2,0.1 0.5,0.0 0.6,-0.2l2.0,-3.5c0.1,-0.2 0.1,-0.5 -0.1,-0.6L19.4,13.0zM12.0,15.5c-1.9,0.0 -3.5,-1.6 -3.5,-3.5s1.6,-3.5 3.5,-3.5s3.5,1.6 3.5,3.5S13.9,15.5 12.0,15.5z"
+     android:fill="#ffffffff"
+     />
+
+</vector>
diff --git a/packages/SystemUI/res/drawable/notification_header_bg.xml b/packages/SystemUI/res/drawable/notification_header_bg.xml
new file mode 100644
index 0000000..09d0d7d
--- /dev/null
+++ b/packages/SystemUI/res/drawable/notification_header_bg.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2014 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_pressed="true">
+        <shape>
+            <solid android:color="@color/background_color_1_press" />
+            <corners android:radius="@*android:dimen/notification_quantum_rounded_rect_radius" />
+        </shape>
+    </item>
+    <item>
+        <shape>
+            <solid android:color="@color/background_color_1" />
+            <corners android:radius="@*android:dimen/notification_quantum_rounded_rect_radius" />
+        </shape>
+    </item>
+</selector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/recents_dismiss_dark.xml b/packages/SystemUI/res/drawable/recents_dismiss_dark.xml
new file mode 100644
index 0000000..744795e
--- /dev/null
+++ b/packages/SystemUI/res/drawable/recents_dismiss_dark.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:versionCode="1" >
+
+    <size
+        android:height="16dp"
+        android:width="16dp" />
+
+    <viewport
+        android:viewportHeight="100"
+        android:viewportWidth="100" />
+
+    <path
+        android:name="x"
+        android:pathData="M0,0L100,100M0,100L100,0z"
+        android:stroke="@color/recents_task_bar_dark_dismiss_color"
+        android:strokeWidth="8.0"
+        android:strokeLineCap="square" />
+
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/recents_dismiss_light.xml b/packages/SystemUI/res/drawable/recents_dismiss_light.xml
new file mode 100644
index 0000000..96bfbe1
--- /dev/null
+++ b/packages/SystemUI/res/drawable/recents_dismiss_light.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:versionCode="1" >
+
+    <size
+        android:height="16dp"
+        android:width="16dp" />
+
+    <viewport
+        android:viewportHeight="100"
+        android:viewportWidth="100" />
+
+
+    <path
+        android:name="x"
+        android:pathData="M0,0L100,100M0,100L100,0z"
+        android:stroke="@color/recents_task_bar_light_dismiss_color"
+        android:strokeWidth="8.0"
+        android:strokeLineCap="square" />
+
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/heads_up.xml b/packages/SystemUI/res/layout/heads_up.xml
index e4954e7..7d9cfa1 100644
--- a/packages/SystemUI/res/layout/heads_up.xml
+++ b/packages/SystemUI/res/layout/heads_up.xml
@@ -20,7 +20,7 @@
 <com.android.systemui.statusbar.policy.HeadsUpNotificationView
         xmlns:android="http://schemas.android.com/apk/res/android"
         android:layout_height="wrap_content"
-        android:layout_width="@dimen/notification_panel_width"
+        android:layout_width="match_parent"
         android:id="@+id/content_holder"
         android:background="@drawable/notification_panel_bg"
         />
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/quick_settings_brightness_dialog.xml b/packages/SystemUI/res/layout/quick_settings_brightness_dialog.xml
index b7df51d..1efda8c 100644
--- a/packages/SystemUI/res/layout/quick_settings_brightness_dialog.xml
+++ b/packages/SystemUI/res/layout/quick_settings_brightness_dialog.xml
@@ -29,7 +29,7 @@
     <com.android.systemui.settings.ToggleSlider
         android:id="@+id/brightness_slider"
         android:layout_width="0dp"
-        android:layout_height="40dp"
+        android:layout_height="44dp"
         android:layout_gravity="center_vertical"
         android:layout_weight="1"
         systemui:text="@string/status_bar_settings_auto_brightness_label" />
diff --git a/packages/SystemUI/res/layout/recents_task_view.xml b/packages/SystemUI/res/layout/recents_task_view.xml
index f7df18eb..bda6431 100644
--- a/packages/SystemUI/res/layout/recents_task_view.xml
+++ b/packages/SystemUI/res/layout/recents_task_view.xml
@@ -63,6 +63,13 @@
             android:maxLines="2"
             android:ellipsize="marquee"
             android:fadingEdge="horizontal" />
+        <ImageView
+            android:id="@+id/dismiss_task"
+            android:layout_width="@dimen/recents_task_view_application_icon_size"
+            android:layout_height="@dimen/recents_task_view_application_icon_size"
+            android:layout_gravity="center_vertical|end"
+            android:padding="23dp"
+            android:src="@drawable/recents_dismiss_dark" />
     </com.android.systemui.recents.views.TaskBarView>
 </com.android.systemui.recents.views.TaskView>
 
diff --git a/packages/SystemUI/res/layout/status_bar.xml b/packages/SystemUI/res/layout/status_bar.xml
index 1b35537..585658e 100644
--- a/packages/SystemUI/res/layout/status_bar.xml
+++ b/packages/SystemUI/res/layout/status_bar.xml
@@ -77,26 +77,24 @@
         <LinearLayout android:id="@+id/system_icon_area"
             android:layout_width="wrap_content"
             android:layout_height="match_parent"
-            android:orientation="horizontal">
-
-            <LinearLayout android:id="@+id/statusIcons"
+            android:orientation="horizontal"
+            >
+            <LinearLayout android:id="@+id/system_icons"
                 android:layout_width="wrap_content"
                 android:layout_height="match_parent"
                 android:gravity="center_vertical"
-                android:orientation="horizontal"/>    
-    
-            <LinearLayout
-                android:id="@+id/signal_battery_cluster"
-                android:layout_width="wrap_content"
-                android:layout_height="match_parent"
-                android:paddingStart="2dp"
-                android:orientation="horizontal"
-                android:gravity="center"
                 >
-                <include layout="@layout/signal_cluster_view" 
+                <LinearLayout android:id="@+id/statusIcons"
+                    android:layout_width="wrap_content"
+                    android:layout_height="match_parent"
+                    android:gravity="center_vertical"
+                    android:orientation="horizontal"/>
+
+                <include layout="@layout/signal_cluster_view"
                     android:id="@+id/signal_cluster"
                     android:layout_width="wrap_content"
                     android:layout_height="wrap_content"
+                    android:layout_marginStart="2dp"
                     />
                 <!-- battery must be padded below to match assets -->
                 <com.android.systemui.BatteryMeterView
@@ -107,7 +105,6 @@
                     android:layout_marginStart="4dip"
                     />
             </LinearLayout>
-    
             <com.android.systemui.statusbar.policy.Clock
                 android:id="@+id/clock"
                 android:textAppearance="@style/TextAppearance.StatusBar.Clock"
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index 3267c36..f045da4 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -22,7 +22,7 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:systemui="http://schemas.android.com/apk/res/com.android.systemui"
     android:id="@+id/notification_panel"
-    android:layout_width="0dp"
+    android:layout_width="match_parent"
     android:layout_height="match_parent"
     >
 
@@ -34,15 +34,6 @@
         android:layout_gravity="bottom"
         />
 
-    <com.android.keyguard.CarrierText
-        android:id="@+id/keyguard_carrier_text"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_marginTop="2dp"
-        android:layout_marginLeft="8dp"
-        android:ellipsize="marquee"
-        android:textAppearance="?android:attr/textAppearanceMedium" />
-
     <include
         layout="@layout/keyguard_status_view"
         android:layout_height="wrap_content"
@@ -59,9 +50,8 @@
         />
 
     <com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer
+        style="@style/NotificationsQuickSettings"
         android:id="@+id/notification_container_parent"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
         android:clipToPadding="false"
         android:clipChildren="false">
 
@@ -98,11 +88,7 @@
 
     </com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer>
 
-
-    <include layout="@layout/status_bar_expanded_header"
-        android:layout_width="match_parent"
-        android:layout_height="@dimen/status_bar_header_height"
-        />
+    <include layout="@layout/status_bar_expanded_header" />
 
     <include
         layout="@layout/keyguard_bottom_area"
diff --git a/packages/SystemUI/res/layout/status_bar_expanded_header.xml b/packages/SystemUI/res/layout/status_bar_expanded_header.xml
index 460dd4b..89fa988 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded_header.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded_header.xml
@@ -20,11 +20,12 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:systemui="http://schemas.android.com/apk/res/com.android.systemui"
     android:id="@+id/header"
-    android:layout_width="match_parent"
+    style="@style/StatusBarHeader"
     android:layout_height="@dimen/status_bar_header_height"
-    android:orientation="horizontal"
-    android:gravity="center_vertical"
+    android:paddingStart="@dimen/notification_side_padding"
+    android:paddingEnd="@dimen/notification_side_padding"
     android:baselineAligned="false"
+    android:elevation="10dp"
     >
 
     <View
@@ -37,10 +38,12 @@
     <RelativeLayout
         android:id="@+id/datetime"
         android:layout_width="wrap_content"
-        android:layout_height="match_parent"
+        android:layout_height="wrap_content"
         android:layout_gravity="start"
-        android:paddingStart="8dp"
-        android:paddingEnd="8dp"
+        android:paddingTop="16dp"
+        android:paddingBottom="16dp"
+        android:paddingStart="16dp"
+        android:paddingEnd="16dp"
         android:background="@drawable/ic_notify_button_bg"
         android:enabled="false"
         >
@@ -48,10 +51,9 @@
             android:id="@+id/clock"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:layout_marginEnd="8dp"
             android:singleLine="true"
             android:textAppearance="@style/TextAppearance.StatusBar.Expanded.Clock"
-            android:layout_centerVertical="true"
+            systemui:amPmStyle="normal"
             />
 
         <com.android.systemui.statusbar.policy.DateView android:id="@+id/date"
@@ -59,11 +61,49 @@
             android:layout_height="wrap_content"
             android:singleLine="true"
             android:textAppearance="@style/TextAppearance.StatusBar.Expanded.Date"
-            android:layout_toEndOf="@id/clock"
-            android:layout_alignBaseline="@id/clock"
+            android:layout_below="@id/clock"
             />
     </RelativeLayout>
 
+    <com.android.keyguard.CarrierText
+        android:id="@+id/keyguard_carrier_text"
+        android:layout_width="wrap_content"
+        android:layout_height="@dimen/status_bar_header_height_keyguard"
+        android:layout_marginLeft="8dp"
+        android:gravity="center_vertical"
+        android:ellipsize="marquee"
+        android:textAppearance="?android:attr/textAppearanceMedium" />
+
+    <com.android.systemui.statusbar.phone.MultiUserSwitch android:id="@+id/multi_user_switch"
+        android:layout_width="40dp"
+        android:layout_height="@dimen/status_bar_header_height"
+        android:layout_alignParentEnd="true"
+        android:background="@null"
+        android:scaleType="centerInside"
+        android:padding="6dp"
+        />
+
+    <ImageButton android:id="@+id/settings_button"
+        style="@android:style/Widget.Quantum.Button.Borderless"
+        android:layout_toStartOf="@id/multi_user_switch"
+        android:layout_width="56dp"
+        android:layout_height="@dimen/status_bar_header_height"
+        android:src="@drawable/ic_settings_24dp"
+        android:contentDescription="@string/accessibility_desc_quick_settings"/>
+
+    <FrameLayout android:id="@+id/system_icons_container"
+        android:layout_width="wrap_content"
+        android:layout_height="@dimen/status_bar_header_height"
+        android:layout_toStartOf="@id/multi_user_switch"
+        android:layout_marginEnd="4dp"
+        />
+
+    <include
+        layout="@layout/quick_settings_brightness_dialog"
+        android:id="@+id/brightness_container"
+        android:layout_width="match_parent"
+        />
+
     <TextView
         android:id="@+id/header_debug_info"
         android:visibility="invisible"
@@ -77,22 +117,4 @@
         android:padding="2dp"
         />
 
-    <include layout="@layout/status_bar_flip_button"
-        android:id="@+id/header_flipper"
-        android:layout_width="50dp"
-        android:layout_height="50dp"
-        android:layout_alignParentEnd="true"/>
-
-    <ImageView android:id="@+id/clear_all_button"
-        android:layout_width="50dp"
-        android:layout_height="50dp"
-        android:layout_toStartOf="@id/header_flipper"
-        android:scaleType="center"
-        android:src="@drawable/ic_notify_clear"
-        android:background="@drawable/ic_notify_button_bg"
-        android:contentDescription="@string/accessibility_clear_all"
-        />
-
-
-
 </com.android.systemui.statusbar.phone.StatusBarHeaderView>
diff --git a/packages/SystemUI/res/layout/status_bar_flip_button.xml b/packages/SystemUI/res/layout/status_bar_flip_button.xml
deleted file mode 100644
index f4d7033..0000000
--- a/packages/SystemUI/res/layout/status_bar_flip_button.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2014 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License
-  -->
-
-<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/settings_button"
-    android:layout_width="50dp"
-    android:layout_height="50dp"
-    android:scaleType="center"
-    android:src="@drawable/ic_notify_quicksettings"
-    android:background="@drawable/ic_notify_button_bg"
-    android:contentDescription="@string/accessibility_desc_quick_settings"/>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/status_bar_toggle_slider.xml b/packages/SystemUI/res/layout/status_bar_toggle_slider.xml
index e6d7c93..0e84762 100644
--- a/packages/SystemUI/res/layout/status_bar_toggle_slider.xml
+++ b/packages/SystemUI/res/layout/status_bar_toggle_slider.xml
@@ -28,6 +28,7 @@
         android:layout_alignParentTop="true"
         android:layout_alignParentBottom="true"
         android:button="@null"
+        android:background="@*android:drawable/switch_track_quantum"
         />
     <com.android.systemui.settings.ToggleSeekBar
         android:id="@+id/slider"
diff --git a/packages/SystemUI/res/layout/super_status_bar.xml b/packages/SystemUI/res/layout/super_status_bar.xml
index f9b022c..26616cd 100644
--- a/packages/SystemUI/res/layout/super_status_bar.xml
+++ b/packages/SystemUI/res/layout/super_status_bar.xml
@@ -35,9 +35,9 @@
         android:layout_width="match_parent"
         android:layout_height="match_parent" >
         <include layout="@layout/status_bar_expanded"
-            android:layout_width="@dimen/notification_panel_width"
+            android:layout_width="match_parent"
             android:layout_height="match_parent"
-            android:layout_gravity="start|top" />
+            android:visibility="gone" />
     </com.android.systemui.statusbar.phone.PanelHolder>
 
 </com.android.systemui.statusbar.phone.StatusBarWindowView>
diff --git a/packages/SystemUI/res/layout/user_switcher_host.xml b/packages/SystemUI/res/layout/user_switcher_host.xml
index bc56cf6..70c5042 100644
--- a/packages/SystemUI/res/layout/user_switcher_host.xml
+++ b/packages/SystemUI/res/layout/user_switcher_host.xml
@@ -22,7 +22,8 @@
         xmlns:tools="http://schemas.android.com/tools"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
-        android:background="#dd000000">
+        android:background="#dd000000"
+        android:elevation="12dp">
     <FrameLayout
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index 8ec48c8..eb66a59 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -78,8 +78,7 @@
     <string name="accessibility_recent" msgid="8571350598987952883">"Onlangse programme"</string>
     <string name="accessibility_search_light" msgid="1103867596330271848">"Deursoek"</string>
     <string name="accessibility_camera_button" msgid="8064671582820358152">"Kamera"</string>
-    <!-- no translation found for accessibility_phone_button (6738112589538563574) -->
-    <skip />
+    <string name="accessibility_phone_button" msgid="6738112589538563574">"Foon"</string>
     <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Knoppie vir wissel van invoermetode."</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Versoenbaarheid-zoem se knoppie."</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Zoem kleiner na groter skerm."</string>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index 7aaaaf3..a3eca6a 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -78,8 +78,7 @@
     <string name="accessibility_recent" msgid="8571350598987952883">"የቅርብ ጊዜ  መተግበሪያዎች"</string>
     <string name="accessibility_search_light" msgid="1103867596330271848">"ፈልግ"</string>
     <string name="accessibility_camera_button" msgid="8064671582820358152">"ካሜራ"</string>
-    <!-- no translation found for accessibility_phone_button (6738112589538563574) -->
-    <skip />
+    <string name="accessibility_phone_button" msgid="6738112589538563574">"ስልክ"</string>
     <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"የግቤት ስልት አዝራር ቀይር"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"የተኳኋኝአጉላ አዝራር።"</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"አነስተኛውን ማያ ወደ ትልቅ አጉላ።"</string>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index febcab6..e03a2f9 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -78,8 +78,7 @@
     <string name="accessibility_recent" msgid="8571350598987952883">"Скорошни приложения"</string>
     <string name="accessibility_search_light" msgid="1103867596330271848">"Търсене"</string>
     <string name="accessibility_camera_button" msgid="8064671582820358152">"Камера"</string>
-    <!-- no translation found for accessibility_phone_button (6738112589538563574) -->
-    <skip />
+    <string name="accessibility_phone_button" msgid="6738112589538563574">"Телефон"</string>
     <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Бутон за превключване на метода на въвеждане."</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Бутон за промяна на мащаба с цел съвместимост."</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Промяна на мащаба на екрана от по-малък до по-голям."</string>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index 658ece0..e50a155 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -78,8 +78,7 @@
     <string name="accessibility_recent" msgid="8571350598987952883">"Aplicacions recents"</string>
     <string name="accessibility_search_light" msgid="1103867596330271848">"Cerca"</string>
     <string name="accessibility_camera_button" msgid="8064671582820358152">"Càmera"</string>
-    <!-- no translation found for accessibility_phone_button (6738112589538563574) -->
-    <skip />
+    <string name="accessibility_phone_button" msgid="6738112589538563574">"Telèfon"</string>
     <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Botó de canvi del mètode d\'entrada."</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Botó de zoom de compatibilitat."</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Amplia menys com més gran sigui la pantalla."</string>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index c7d2703..a2d9d9c 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -78,8 +78,7 @@
     <string name="accessibility_recent" msgid="8571350598987952883">"Nové aplikace"</string>
     <string name="accessibility_search_light" msgid="1103867596330271848">"Hledat"</string>
     <string name="accessibility_camera_button" msgid="8064671582820358152">"Fotoaparát"</string>
-    <!-- no translation found for accessibility_phone_button (6738112589538563574) -->
-    <skip />
+    <string name="accessibility_phone_button" msgid="6738112589538563574">"Telefon"</string>
     <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Tlačítko přepnutí metody zadávání"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Tlačítko úpravy velikosti z důvodu kompatibility"</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Zvětšit menší obrázek na větší obrazovku."</string>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index 51198e6..00041a2 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -78,8 +78,7 @@
     <string name="accessibility_recent" msgid="8571350598987952883">"Aplicaciones recientes"</string>
     <string name="accessibility_search_light" msgid="1103867596330271848">"Buscar"</string>
     <string name="accessibility_camera_button" msgid="8064671582820358152">"Cámara"</string>
-    <!-- no translation found for accessibility_phone_button (6738112589538563574) -->
-    <skip />
+    <string name="accessibility_phone_button" msgid="6738112589538563574">"Teléfono"</string>
     <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Botón Cambiar método de entrada"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Botón de zoom de compatibilidad"</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Zoom de pantalla más pequeña a más grande"</string>
diff --git a/packages/SystemUI/res/values-et-rEE/strings.xml b/packages/SystemUI/res/values-et-rEE/strings.xml
index 0f95c60..4c42a32 100644
--- a/packages/SystemUI/res/values-et-rEE/strings.xml
+++ b/packages/SystemUI/res/values-et-rEE/strings.xml
@@ -78,8 +78,7 @@
     <string name="accessibility_recent" msgid="8571350598987952883">"Hiljutised rakendused"</string>
     <string name="accessibility_search_light" msgid="1103867596330271848">"Otsing"</string>
     <string name="accessibility_camera_button" msgid="8064671582820358152">"Kaamera"</string>
-    <!-- no translation found for accessibility_phone_button (6738112589538563574) -->
-    <skip />
+    <string name="accessibility_phone_button" msgid="6738112589538563574">"Telefon"</string>
     <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Sisestusmeetodi vahetamise nupp."</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Sobivussuumi nupp."</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Suumi suuremale ekraanile vähem."</string>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index f36d04b..a9c6af7a 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -78,8 +78,7 @@
     <string name="accessibility_recent" msgid="8571350598987952883">"برنامه‌های اخیر"</string>
     <string name="accessibility_search_light" msgid="1103867596330271848">"جستجو"</string>
     <string name="accessibility_camera_button" msgid="8064671582820358152">"دوربین"</string>
-    <!-- no translation found for accessibility_phone_button (6738112589538563574) -->
-    <skip />
+    <string name="accessibility_phone_button" msgid="6738112589538563574">"تلفن"</string>
     <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"کلید تغییر روش ورود متن."</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"دکمه بزرگنمایی سازگار."</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"بزرگنمایی از صفحه‌های کوچک تا بزرگ."</string>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index 4ae3851..8ddf070 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -78,8 +78,7 @@
     <string name="accessibility_recent" msgid="8571350598987952883">"Viimeaikaiset sovellukset"</string>
     <string name="accessibility_search_light" msgid="1103867596330271848">"Haku"</string>
     <string name="accessibility_camera_button" msgid="8064671582820358152">"Kamera"</string>
-    <!-- no translation found for accessibility_phone_button (6738112589538563574) -->
-    <skip />
+    <string name="accessibility_phone_button" msgid="6738112589538563574">"Puhelin"</string>
     <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Syöttötavan vaihtopainike."</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Yhteensopivuuszoomaus-painike."</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Zoomaa pienemmältä suuremmalle ruudulle."</string>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index c55a5fc..69b5acc 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -78,8 +78,7 @@
     <string name="accessibility_recent" msgid="8571350598987952883">"Applications récentes"</string>
     <string name="accessibility_search_light" msgid="1103867596330271848">"Rechercher"</string>
     <string name="accessibility_camera_button" msgid="8064671582820358152">"Appareil photo"</string>
-    <!-- no translation found for accessibility_phone_button (6738112589538563574) -->
-    <skip />
+    <string name="accessibility_phone_button" msgid="6738112589538563574">"Téléphone"</string>
     <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Bouton \"Changer le mode de saisie\""</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Bouton \"Zoom de compatibilité\""</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Zoom de compatibilité avec la taille de l\'écran"</string>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index 1121918..85f817b 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -78,8 +78,7 @@
     <string name="accessibility_recent" msgid="8571350598987952883">"Applications récentes"</string>
     <string name="accessibility_search_light" msgid="1103867596330271848">"Rechercher"</string>
     <string name="accessibility_camera_button" msgid="8064671582820358152">"Appareil photo"</string>
-    <!-- no translation found for accessibility_phone_button (6738112589538563574) -->
-    <skip />
+    <string name="accessibility_phone_button" msgid="6738112589538563574">"Téléphoner"</string>
     <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Bouton \"Changer le mode de saisie\""</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Bouton \"Zoom de compatibilité\""</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Zoom de compatibilité avec la taille de l\'écran"</string>
diff --git a/packages/SystemUI/res/values-hy-rAM/strings.xml b/packages/SystemUI/res/values-hy-rAM/strings.xml
index 7a8ab10..8c26a8a 100644
--- a/packages/SystemUI/res/values-hy-rAM/strings.xml
+++ b/packages/SystemUI/res/values-hy-rAM/strings.xml
@@ -78,8 +78,7 @@
     <string name="accessibility_recent" msgid="8571350598987952883">"Վերջին ծրագրերը"</string>
     <string name="accessibility_search_light" msgid="1103867596330271848">"Որոնել"</string>
     <string name="accessibility_camera_button" msgid="8064671582820358152">"Ֆոտոխցիկ"</string>
-    <!-- no translation found for accessibility_phone_button (6738112589538563574) -->
-    <skip />
+    <string name="accessibility_phone_button" msgid="6738112589538563574">"Հեռախոս"</string>
     <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Միացնել մուտքագրման եղանակի կոճակը:"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Համատեղելիության խոշորացման կոճակը:"</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Դիտափոխել փոքրից ավելի մեծ էկրան:"</string>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index 08ffc54..e1625076 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -78,8 +78,7 @@
     <string name="accessibility_recent" msgid="8571350598987952883">"אפליקציות אחרונות"</string>
     <string name="accessibility_search_light" msgid="1103867596330271848">"חפש"</string>
     <string name="accessibility_camera_button" msgid="8064671582820358152">"מצלמה"</string>
-    <!-- no translation found for accessibility_phone_button (6738112589538563574) -->
-    <skip />
+    <string name="accessibility_phone_button" msgid="6738112589538563574">"טלפון"</string>
     <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"לחצן החלפת שיטת קלט."</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"לחצן מרחק מתצוגה של תאימות."</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"שנה מרחק מתצוגה של מסך קטן לגדול יותר."</string>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index 2caa069..a9e7935 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -78,8 +78,7 @@
     <string name="accessibility_recent" msgid="8571350598987952883">"最近使ったアプリ"</string>
     <string name="accessibility_search_light" msgid="1103867596330271848">"検索"</string>
     <string name="accessibility_camera_button" msgid="8064671582820358152">"カメラ"</string>
-    <!-- no translation found for accessibility_phone_button (6738112589538563574) -->
-    <skip />
+    <string name="accessibility_phone_button" msgid="6738112589538563574">"電話"</string>
     <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"入力方法の切り替えボタン。"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"互換ズームボタン。"</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"小さい画面から大きい画面に拡大。"</string>
diff --git a/packages/SystemUI/res/values-ka-rGE/strings.xml b/packages/SystemUI/res/values-ka-rGE/strings.xml
index 62e40d0..c56864b 100644
--- a/packages/SystemUI/res/values-ka-rGE/strings.xml
+++ b/packages/SystemUI/res/values-ka-rGE/strings.xml
@@ -78,8 +78,7 @@
     <string name="accessibility_recent" msgid="8571350598987952883">"ბოლოს გამოყენებული აპები"</string>
     <string name="accessibility_search_light" msgid="1103867596330271848">"ძიება"</string>
     <string name="accessibility_camera_button" msgid="8064671582820358152">"კამერა"</string>
-    <!-- no translation found for accessibility_phone_button (6738112589538563574) -->
-    <skip />
+    <string name="accessibility_phone_button" msgid="6738112589538563574">"ტელეფონი"</string>
     <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"შეყვანის მეთოდის გადართვის ღილაკი."</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"თავსებადი მასშტაბირების ღილაკი."</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"შეცვალეთ პატარა ეკრანი უფრო დიდით."</string>
diff --git a/packages/SystemUI/res/values-km-rKH/strings.xml b/packages/SystemUI/res/values-km-rKH/strings.xml
index 35a6917..a5008b3 100644
--- a/packages/SystemUI/res/values-km-rKH/strings.xml
+++ b/packages/SystemUI/res/values-km-rKH/strings.xml
@@ -64,7 +64,7 @@
     <string name="screenshot_saving_ticker" msgid="7403652894056693515">"កំពុង​រក្សាទុក​រូបថត​អេក្រង់…"</string>
     <string name="screenshot_saving_title" msgid="8242282144535555697">"កំពុង​រក្សាទុក​រូបថត​អេក្រង់..."</string>
     <string name="screenshot_saving_text" msgid="2419718443411738818">"រូបថត​អេក្រង់​កំពុង​ត្រូវ​បាន​រក្សាទុក។"</string>
-    <string name="screenshot_saved_title" msgid="6461865960961414961">"បាន​ចាប់​យក​រូបថត​អេក្រង់។"</string>
+    <string name="screenshot_saved_title" msgid="6461865960961414961">"បាន​ចាប់​យក​រូបថត​អេក្រង់។​"</string>
     <string name="screenshot_saved_text" msgid="1152839647677558815">"ប៉ះ ​ដើម្បី​មើល​រូបថត​អេក្រង់​របស់​អ្នក​។"</string>
     <string name="screenshot_failed_title" msgid="705781116746922771">"មិន​អាច​ចាប់​យក​រូប​ថត​អេក្រង់​។"</string>
     <string name="screenshot_failed_text" msgid="8134011269572415402">"មិន​អាច​រក្សាទុក​រូបថត​អេក្រង់​។ ឧបករណ៍​ផ្ទុក​អាច​កំពុង​ប្រើ​​។"</string>
@@ -78,8 +78,7 @@
     <string name="accessibility_recent" msgid="8571350598987952883">"កម្មវិធី​ថ្មីៗ"</string>
     <string name="accessibility_search_light" msgid="1103867596330271848">"ស្វែងរក"</string>
     <string name="accessibility_camera_button" msgid="8064671582820358152">"ម៉ាស៊ីន​ថត"</string>
-    <!-- no translation found for accessibility_phone_button (6738112589538563574) -->
-    <skip />
+    <string name="accessibility_phone_button" msgid="6738112589538563574">"ទូរស័ព្ទ"</string>
     <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"ប្ដូរ​ប៊ូតុង​វិធីសាស្ត្រ​បញ្ចូល។"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"ប៊ូតុង​ពង្រីក​ត្រូវ​គ្នា។"</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"ពង្រីក/បង្រួម​​អេក្រង់​ពី​​ទៅធំ"</string>
@@ -141,7 +140,7 @@
     <string name="accessibility_remove_notification" msgid="3603099514902182350">"សម្អាត​ការ​ជូន​ដំណឹង។"</string>
     <string name="accessibility_gps_enabled" msgid="3511469499240123019">"បាន​បើក GPS ។"</string>
     <string name="accessibility_gps_acquiring" msgid="8959333351058967158">"ទទួល​​ GPS ។"</string>
-    <string name="accessibility_tty_enabled" msgid="4613200365379426561">"បាន​បើក​ម៉ាស៊ីន​អង្គុលីលេខ"</string>
+    <string name="accessibility_tty_enabled" msgid="4613200365379426561">"បាន​បើក​ម៉ាស៊ីន​អង្គុលីលេខ​"</string>
     <string name="accessibility_ringer_vibrate" msgid="666585363364155055">"កម្មវិធី​រោទ៍​ញ័រ។"</string>
     <string name="accessibility_ringer_silent" msgid="9061243307939135383">"កម្មវិធី​រោទ៍​ស្ងាត់។"</string>
     <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> បដិសេធ។"</string>
@@ -188,7 +187,7 @@
     <string name="quick_settings_rotation_locked_portrait_label" msgid="1553131290066230775">"ចាក់​សោ​​បញ្ឈរ"</string>
     <string name="quick_settings_rotation_locked_landscape_label" msgid="7216265671276086593">"ចាក់​សោ​​​ផ្ដេក"</string>
     <string name="quick_settings_ime_label" msgid="7073463064369468429">"វិធីសាស្ត្រ​បញ្ចូល"</string>
-    <string name="quick_settings_location_label" msgid="5011327048748762257">"ទី​តាំង"</string>
+    <string name="quick_settings_location_label" msgid="5011327048748762257">"ទី​តាំង​"</string>
     <string name="quick_settings_location_off_label" msgid="7464544086507331459">"ទីតាំង​បាន​បិទ"</string>
     <string name="quick_settings_media_device_label" msgid="1302906836372603762">"ឧបករណ៍​មេឌៀ"</string>
     <string name="quick_settings_rssi_label" msgid="7725671335550695589">"RSSI"</string>
@@ -209,7 +208,7 @@
     <string name="recents_empty_message" msgid="2269156590813544104">"ថ្មីៗ"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"ព័ត៌មាន​កម្មវិធី"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"ស្វែងរក"</string>
-    <string name="ssl_ca_cert_warning" msgid="9005954106902053641">"បណ្ដាញ​អាច​\nត្រូវ​បាន​ត្រួតពិនិត្យ"</string>
+    <string name="ssl_ca_cert_warning" msgid="9005954106902053641">"បណ្ដាញ​អាច​\nត្រូវ​បាន​ត្រួតពិនិត្យ​"</string>
     <string name="description_target_search" msgid="3091587249776033139">"ស្វែងរក"</string>
     <string name="description_direction_up" msgid="7169032478259485180">"រុញ​ឡើង​លើ​ដើម្បី <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> ។"</string>
     <string name="description_direction_left" msgid="7207478719805562165">"រុញ​ទៅ​ឆ្វេង​ដើម្បី <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> ។"</string>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index d7cd13b..bd7a257 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -78,8 +78,7 @@
     <string name="accessibility_recent" msgid="8571350598987952883">"최근에 사용한 앱"</string>
     <string name="accessibility_search_light" msgid="1103867596330271848">"검색"</string>
     <string name="accessibility_camera_button" msgid="8064671582820358152">"카메라"</string>
-    <!-- no translation found for accessibility_phone_button (6738112589538563574) -->
-    <skip />
+    <string name="accessibility_phone_button" msgid="6738112589538563574">"전화"</string>
     <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"입력 방법 버튼을 전환합니다."</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"호환성 확대/축소 버튼입니다."</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"작은 화면을 큰 화면으로 확대합니다."</string>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index 87087b4..b160a5a 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -181,7 +181,7 @@
     <string name="quick_settings_bluetooth_label" msgid="6304190285170721401">"Bluetooth"</string>
     <string name="quick_settings_bluetooth_multiple_devices_label" msgid="3912245565613684735">"„Bluetooth“ (<xliff:g id="NUMBER">%d</xliff:g> įreng.)"</string>
     <string name="quick_settings_bluetooth_off_label" msgid="8159652146149219937">"„Bluetooth“ išjungta"</string>
-    <string name="quick_settings_brightness_label" msgid="6968372297018755815">"Skaistis"</string>
+    <string name="quick_settings_brightness_label" msgid="6968372297018755815">"Šviesumas"</string>
     <string name="quick_settings_rotation_unlocked_label" msgid="336054930362580584">"Automatiškai sukti"</string>
     <string name="quick_settings_rotation_locked_label" msgid="8058646447242565486">"Sukimas užrakintas"</string>
     <string name="quick_settings_rotation_locked_portrait_label" msgid="1553131290066230775">"Užrakinta stačia padėtis"</string>
@@ -200,7 +200,7 @@
     <string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Tinklo nėra"</string>
     <string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"„Wi-Fi“ išjungta"</string>
     <string name="quick_settings_remote_display_no_connection_label" msgid="372107699274391290">"Perduoti ekraną"</string>
-    <string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Skaistis"</string>
+    <string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Šviesumas"</string>
     <string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTOMATINIS"</string>
     <string name="quick_settings_inversion_label" msgid="1666358784283020762">"Spalvų inversijos režimas"</string>
     <string name="quick_settings_contrast_label" msgid="3319507551689108692">"Patobulinto kontrasto režimas"</string>
diff --git a/packages/SystemUI/res/values-mn-rMN/strings.xml b/packages/SystemUI/res/values-mn-rMN/strings.xml
index 3e7b660..4669d99 100644
--- a/packages/SystemUI/res/values-mn-rMN/strings.xml
+++ b/packages/SystemUI/res/values-mn-rMN/strings.xml
@@ -78,8 +78,7 @@
     <string name="accessibility_recent" msgid="8571350598987952883">"Сүүлийн апп"</string>
     <string name="accessibility_search_light" msgid="1103867596330271848">"Хайх"</string>
     <string name="accessibility_camera_button" msgid="8064671582820358152">"Камер"</string>
-    <!-- no translation found for accessibility_phone_button (6738112589538563574) -->
-    <skip />
+    <string name="accessibility_phone_button" msgid="6738112589538563574">"Утас"</string>
     <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Оруулах аргыг сэлгэх товч."</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Тохиромжтой өсгөх товч."</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Жижгээс том дэлгэцрүү өсгөх."</string>
diff --git a/packages/SystemUI/res/values-ms-rMY/strings.xml b/packages/SystemUI/res/values-ms-rMY/strings.xml
index af50786..60e5527 100644
--- a/packages/SystemUI/res/values-ms-rMY/strings.xml
+++ b/packages/SystemUI/res/values-ms-rMY/strings.xml
@@ -78,8 +78,7 @@
     <string name="accessibility_recent" msgid="8571350598987952883">"Aplikasi terbaharu"</string>
     <string name="accessibility_search_light" msgid="1103867596330271848">"Cari"</string>
     <string name="accessibility_camera_button" msgid="8064671582820358152">"Kamera"</string>
-    <!-- no translation found for accessibility_phone_button (6738112589538563574) -->
-    <skip />
+    <string name="accessibility_phone_button" msgid="6738112589538563574">"Telefon"</string>
     <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Butang tukar kaedah input."</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Butang zum keserasian."</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Skrin zum lebih kecil kepada lebih besar."</string>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index e51341d..00bea9c 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -78,8 +78,7 @@
     <string name="accessibility_recent" msgid="8571350598987952883">"Ostatnie aplikacje"</string>
     <string name="accessibility_search_light" msgid="1103867596330271848">"Szukaj"</string>
     <string name="accessibility_camera_button" msgid="8064671582820358152">"Aparat"</string>
-    <!-- no translation found for accessibility_phone_button (6738112589538563574) -->
-    <skip />
+    <string name="accessibility_phone_button" msgid="6738112589538563574">"Telefon"</string>
     <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Przycisk przełączania metody wprowadzania."</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Przycisk powiększenia na potrzeby zgodności."</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Powiększa mniejszy ekran do większego."</string>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index 162d763..2337a7d 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -78,8 +78,7 @@
     <string name="accessibility_recent" msgid="8571350598987952883">"Aplicativos recentes"</string>
     <string name="accessibility_search_light" msgid="1103867596330271848">"Pesquisar"</string>
     <string name="accessibility_camera_button" msgid="8064671582820358152">"Câmera"</string>
-    <!-- no translation found for accessibility_phone_button (6738112589538563574) -->
-    <skip />
+    <string name="accessibility_phone_button" msgid="6738112589538563574">"Telefone"</string>
     <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Alterar botão do método de entrada."</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Botão de zoom da compatibilidade."</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Aumentar a tela com zoom."</string>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index 75ba35b..4f2c471 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -78,8 +78,7 @@
     <string name="accessibility_recent" msgid="8571350598987952883">"Aplicaţii recente"</string>
     <string name="accessibility_search_light" msgid="1103867596330271848">"Căutați"</string>
     <string name="accessibility_camera_button" msgid="8064671582820358152">"Cameră foto"</string>
-    <!-- no translation found for accessibility_phone_button (6738112589538563574) -->
-    <skip />
+    <string name="accessibility_phone_button" msgid="6738112589538563574">"Telefon"</string>
     <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Buton pentru comutarea metodei de introducere."</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Buton zoom pentru compatibilitate."</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Faceţi zoom de la o imagine mai mică la una mai mare."</string>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index 6bdc6f4..837e98e 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -78,8 +78,7 @@
     <string name="accessibility_recent" msgid="8571350598987952883">"Недавние приложения"</string>
     <string name="accessibility_search_light" msgid="1103867596330271848">"Поиск"</string>
     <string name="accessibility_camera_button" msgid="8064671582820358152">"Камера"</string>
-    <!-- no translation found for accessibility_phone_button (6738112589538563574) -->
-    <skip />
+    <string name="accessibility_phone_button" msgid="6738112589538563574">"Телефон."</string>
     <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Кнопка переключения способа ввода."</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Кнопка масштабирования (режим совместимости)"</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Уменьшение изображения для увеличения свободного места на экране."</string>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index 12c90ec..ae70ed6 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -78,8 +78,7 @@
     <string name="accessibility_recent" msgid="8571350598987952883">"Nové aplikácie"</string>
     <string name="accessibility_search_light" msgid="1103867596330271848">"Hľadať"</string>
     <string name="accessibility_camera_button" msgid="8064671582820358152">"Fotoaparát"</string>
-    <!-- no translation found for accessibility_phone_button (6738112589538563574) -->
-    <skip />
+    <string name="accessibility_phone_button" msgid="6738112589538563574">"Telefón"</string>
     <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Tlačidlo prepnutia metódy vstupu."</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Tlačidlo úpravy veľkosti z dôvodu kompatibility."</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Zväčšiť menší obrázok na väčšiu obrazovku."</string>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index b626f78..2713cf9 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -76,8 +76,7 @@
     <string name="accessibility_recent" msgid="8571350598987952883">"Programu za hivi karibuni"</string>
     <string name="accessibility_search_light" msgid="1103867596330271848">"Tafuta"</string>
     <string name="accessibility_camera_button" msgid="8064671582820358152">"Kamera"</string>
-    <!-- no translation found for accessibility_phone_button (6738112589538563574) -->
-    <skip />
+    <string name="accessibility_phone_button" msgid="6738112589538563574">"Simu"</string>
     <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Swichi kitufe cha mbinu ingizi."</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Kichupo cha kukuza kwa utangamanifu"</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Kuza kidogo kwa skrini kubwa."</string>
diff --git a/packages/SystemUI/res/values-sw600dp/styles.xml b/packages/SystemUI/res/values-sw600dp/styles.xml
index b7becac..d4a99866 100644
--- a/packages/SystemUI/res/values-sw600dp/styles.xml
+++ b/packages/SystemUI/res/values-sw600dp/styles.xml
@@ -18,4 +18,15 @@
     <style name="BrightnessDialogContainer" parent="@style/BaseBrightnessDialogContainer">
         <item name="android:layout_width">480dp</item>
     </style>
+
+    <style name="NotificationsQuickSettings">
+        <item name="android:layout_width">@dimen/notification_panel_width</item>
+        <item name="android:layout_height">match_parent</item>
+        <item name="android:layout_gravity">top|center_horizontal</item>
+    </style>
+
+    <style name="StatusBarHeader">
+        <item name="android:layout_width">@dimen/notification_panel_width</item>
+        <item name="android:layout_gravity">center_horizontal</item>
+    </style>
 </resources>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index 6cb4a48..4dc3d22 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -78,8 +78,7 @@
     <string name="accessibility_recent" msgid="8571350598987952883">"แอปพลิเคชันล่าสุด"</string>
     <string name="accessibility_search_light" msgid="1103867596330271848">"ค้นหา"</string>
     <string name="accessibility_camera_button" msgid="8064671582820358152">"กล้องถ่ายรูป"</string>
-    <!-- no translation found for accessibility_phone_button (6738112589538563574) -->
-    <skip />
+    <string name="accessibility_phone_button" msgid="6738112589538563574">"โทรศัพท์"</string>
     <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"ปุ่มสลับวิธีการป้อนข้อมูล"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"ปุ่มซูมที่ใช้งานร่วมกันได้"</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"ซูมหน้าจอให้มีขนาดใหญ่ขึ้น"</string>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index 17e720e..e50f723 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -78,8 +78,7 @@
     <string name="accessibility_recent" msgid="8571350598987952883">"Kamakailang apps"</string>
     <string name="accessibility_search_light" msgid="1103867596330271848">"Hanapin"</string>
     <string name="accessibility_camera_button" msgid="8064671582820358152">"Camera"</string>
-    <!-- no translation found for accessibility_phone_button (6738112589538563574) -->
-    <skip />
+    <string name="accessibility_phone_button" msgid="6738112589538563574">"Telepono"</string>
     <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Ilipat ang button na pamamaraan ng pag-input."</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Button ng zoom ng pagiging tugma."</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Mag-zoom nang mas maliit sa mas malaking screen."</string>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index 1bf033f5..9100055 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -78,8 +78,7 @@
     <string name="accessibility_recent" msgid="8571350598987952883">"Son uygulamalar"</string>
     <string name="accessibility_search_light" msgid="1103867596330271848">"Ara"</string>
     <string name="accessibility_camera_button" msgid="8064671582820358152">"Kamera"</string>
-    <!-- no translation found for accessibility_phone_button (6738112589538563574) -->
-    <skip />
+    <string name="accessibility_phone_button" msgid="6738112589538563574">"Telefon"</string>
     <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Giriş yöntemini değiştirme düğmesi."</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Uyumluluk zum düğmesi."</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Daha büyük ekrana daha küçük yakınlaştır."</string>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index 8df2449..a4aadb3 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -78,8 +78,7 @@
     <string name="accessibility_recent" msgid="8571350598987952883">"Ứng dụng gần đây"</string>
     <string name="accessibility_search_light" msgid="1103867596330271848">"Tìm kiếm"</string>
     <string name="accessibility_camera_button" msgid="8064671582820358152">"Máy ảnh"</string>
-    <!-- no translation found for accessibility_phone_button (6738112589538563574) -->
-    <skip />
+    <string name="accessibility_phone_button" msgid="6738112589538563574">"Điện thoại"</string>
     <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"Nút chuyển phương thức nhập."</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"Nút thu phóng khả năng tương thích."</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"Thu phóng màn hình lớn hơn hoặc nhỏ hơn."</string>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index 8643ca8..a5ae2b7 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -78,8 +78,7 @@
     <string name="accessibility_recent" msgid="8571350598987952883">"最近运行的应用"</string>
     <string name="accessibility_search_light" msgid="1103867596330271848">"搜索"</string>
     <string name="accessibility_camera_button" msgid="8064671582820358152">"相机"</string>
-    <!-- no translation found for accessibility_phone_button (6738112589538563574) -->
-    <skip />
+    <string name="accessibility_phone_button" msgid="6738112589538563574">"电话"</string>
     <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"输入法切换按钮。"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"兼容性缩放按钮。"</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"将小屏幕的图片放大在较大屏幕上显示。"</string>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index 07b7841..1e3f455 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -78,8 +78,7 @@
     <string name="accessibility_recent" msgid="8571350598987952883">"最近使用的應用程式"</string>
     <string name="accessibility_search_light" msgid="1103867596330271848">"搜尋"</string>
     <string name="accessibility_camera_button" msgid="8064671582820358152">"相機"</string>
-    <!-- no translation found for accessibility_phone_button (6738112589538563574) -->
-    <skip />
+    <string name="accessibility_phone_button" msgid="6738112589538563574">"電話"</string>
     <string name="accessibility_ime_switch_button" msgid="5032926134740456424">"切換輸入法按鈕。"</string>
     <string name="accessibility_compatibility_zoom_button" msgid="8461115318742350699">"相容性縮放按鈕。"</string>
     <string name="accessibility_compatibility_zoom_example" msgid="4220687294564945780">"將較小螢幕的畫面放大在較大螢幕上顯示。"</string>
diff --git a/packages/SystemUI/res/values/attrs.xml b/packages/SystemUI/res/values/attrs.xml
index f5674d2..8fd1206 100644
--- a/packages/SystemUI/res/values/attrs.xml
+++ b/packages/SystemUI/res/values/attrs.xml
@@ -51,6 +51,13 @@
             <enum name="end" value="1" />
         </attr>
     </declare-styleable>
+    <declare-styleable name="Clock">
+        <attr name="amPmStyle" format="enum">
+            <enum name="normal" value="0" />
+            <enum name="small" value="1" />
+            <enum name="gone" value="2" />
+        </attr>
+    </declare-styleable>
     <attr name="orientation">
         <enum name="horizontal" value="0" />
         <enum name="vertical" value="1" />
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 9281265..c1a4e26 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -30,7 +30,6 @@
     <drawable name="recents_callout_line">#99ffffff</drawable>
     <drawable name="notification_item_background_legacy_color">#ffaaaaaa</drawable>
     <drawable name="heads_up_notification_bg_pressed">#ff33B5E5</drawable>
-    <drawable name="notification_header_bg">#FF000000</drawable>
     <color name="notification_panel_scrim_color">#A0000000</color>
     <color name="notification_panel_scrim_color_keyguard">#80000000</color>
     <color name="batterymeter_frame_color">#66FFFFFF</color><!-- 40% white -->
@@ -53,10 +52,18 @@
     <!-- The default recents task bar background color. -->
     <color name="recents_task_bar_default_background_color">#e6444444</color>
     <!-- The default recents task bar text color. -->
-    <color name="recents_task_bar_default_text_color">#ffffffff</color>
+    <color name="recents_task_bar_default_text_color">#ffeeeeee</color>
     <!-- The recents task bar light text color to be drawn on top of dark backgrounds. -->
-    <color name="recents_task_bar_light_text_color">#ffffffff</color>
+    <color name="recents_task_bar_light_text_color">#ffeeeeee</color>
     <!-- The recents task bar dark text color to be drawn on top of light backgrounds. -->
     <color name="recents_task_bar_dark_text_color">#ff222222</color>
+    <!-- The recents task bar light dismiss icon color to be drawn on top of dark backgrounds. -->
+    <color name="recents_task_bar_light_dismiss_color">#ffeeeeee</color>
+    <!-- The recents task bar dark dismiss icon color to be drawn on top of light backgrounds. -->
+    <color name="recents_task_bar_dark_dismiss_color">#ff333333</color>
 
+    <!-- Our quantum color palette (deep teal) -->
+    <color name="primary_color">#ff7fcac3</color>
+    <color name="background_color_1">#ff384248</color>
+    <color name="background_color_1_press">#ff54656e</color>
 </resources>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index c0376f0..21eb41c 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -119,7 +119,7 @@
     <!-- The animation duration for animating in the info pane. -->
     <integer name="recents_animate_task_view_info_pane_duration">150</integer>
     <!-- The animation duration for animating the removal of a task view. -->
-    <integer name="recents_animate_task_view_remove_duration">150</integer>
+    <integer name="recents_animate_task_view_remove_duration">250</integer>
     <!-- The minimum alpha for the dim applied to cards that go deeper into the stack. -->
     <integer name="recents_max_task_stack_view_dim">96</integer>
     <!-- Transposes the search bar layout in landscape -->
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index c6fdc16..ab34030 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -153,11 +153,14 @@
     <dimen name="close_handle_underlap">32dp</dimen>
 
     <!-- Height of the status bar header bar -->
-    <dimen name="status_bar_header_height">48dp</dimen>
+    <dimen name="status_bar_header_height">56dp</dimen>
 
     <!-- Height of the status bar header bar when expanded -->
     <dimen name="status_bar_header_height_expanded">144dp</dimen>
 
+    <!-- Height of the status bar header bar when on Keyguard -->
+    <dimen name="status_bar_header_height_keyguard">40dp</dimen>
+
     <!-- Gravity for the notification panel -->
     <!-- 0x37 = fill_horizontal|top -->
     <integer name="notification_panel_layout_gravity">0x37</integer>
@@ -196,9 +199,6 @@
     <!-- Quick Settings CA Cert Warning tile geometry: gap between icon and text -->
     <dimen name="qs_cawarn_tile_margin_below_icon">3dp</dimen>
 
-    <!-- The width of the notification panel window: match_parent below sw600dp -->
-    <dimen name="notification_panel_width">-1dp</dimen>
-
     <!-- used by DessertCase -->
     <dimen name="dessert_case_cell_size">192dp</dimen>
 
@@ -224,7 +224,7 @@
     <dimen name="recents_task_view_z_increment">5dp</dimen>
 
     <!-- The amount to translate when animating the removal of a task. -->
-    <dimen name="recents_task_view_remove_anim_translation_x">75dp</dimen>
+    <dimen name="recents_task_view_remove_anim_translation_x">100dp</dimen>
 
     <!-- The amount of space a user has to scroll to dismiss any info panes. -->
     <dimen name="recents_task_stack_scroll_dismiss_info_pane_distance">50dp</dimen>
@@ -243,7 +243,10 @@
     <dimen name="top_stack_peek_amount">12dp</dimen>
 
     <!-- Space reserved for the cards behind the top card in the bottom stack -->
-    <dimen name="bottom_stack_peek_amount">18dp</dimen>
+    <dimen name="bottom_stack_peek_amount">12dp</dimen>
+
+    <!-- The height of the area before the bottom stack in which the notifications slow down -->
+    <dimen name="bottom_stack_slow_down_length">12dp</dimen>
 
     <!-- The side padding of the notifications-->
     <dimen name="notification_side_padding">8dp</dimen>
@@ -251,8 +254,11 @@
     <!-- Z distance between notifications if they are in the stack -->
     <dimen name="z_distance_between_notifications">2dp</dimen>
 
+    <!-- The padding between the individual notification cards when dimmed. -->
+    <dimen name="notification_padding_dimmed">0dp</dimen>
+
     <!-- The padding between the individual notification cards. -->
-    <dimen name="notification_padding">3dp</dimen>
+    <dimen name="notification_padding">4dp</dimen>
 
     <!-- The total height of the stack in its collapsed size (i.e. when quick settings is open) -->
     <dimen name="collapsed_stack_height">94dp</dimen>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 3f0a60f..3d3cdf6 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -504,8 +504,6 @@
     <string name="quick_settings_brightness_dialog_auto_brightness_label">AUTO</string>
     <!-- QuickSettings: Label for the toggle that controls whether display inversion is enabled. [CHAR LIMIT=NONE] -->
     <string name="quick_settings_inversion_label">Color inversion mode</string>
-    <!-- QuickSettings: Label for the toggle that controls whether display contrast enhancement is enabled. [CHAR LIMIT=NONE] -->
-    <string name="quick_settings_contrast_label">Enhanced contrast mode</string>
     <!-- QuickSettings: Label for the toggle that controls whether display color correction is enabled. [CHAR LIMIT=NONE] -->
     <string name="quick_settings_color_space_label">Color correction mode</string>
 
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 8ab646d..4f52870 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -69,8 +69,7 @@
     <style name="TextAppearance.StatusBar.Expanded" parent="@*android:style/TextAppearance.StatusBar" />
 
     <style name="TextAppearance.StatusBar.Expanded.Clock">
-        <item name="android:textSize">32dp</item>
-        <item name="android:fontFamily">sans-serif-light</item>
+        <item name="android:textSize">18dp</item>
         <item name="android:textStyle">normal</item>
         <item name="android:textColor">#ffffff</item>
     </style>
@@ -78,8 +77,7 @@
     <style name="TextAppearance.StatusBar.Expanded.Date">
         <item name="android:textSize">12dp</item>
         <item name="android:textStyle">normal</item>
-        <item name="android:textColor">#cccccc</item>
-        <item name="android:textAllCaps">true</item>
+        <item name="android:textColor">#afb3b6</item>
     </style>
 
     <style name="TextAppearance.StatusBar.Expanded.Network" parent="@style/TextAppearance.StatusBar.Expanded.Date">
@@ -138,6 +136,7 @@
         <item name="android:layout_width">match_parent</item>
         <item name="android:layout_height">wrap_content</item>
         <item name="android:padding">16dp</item>
+        <item name="android:layout_alignParentBottom">true</item>
     </style>
 
     <style name="BrightnessDialogContainer" parent="@style/BaseBrightnessDialogContainer" />
@@ -169,6 +168,16 @@
         <item name="android:textSize">14dp</item>
     </style>
 
-    <style name="systemui_theme" parent="@android:style/Theme.DeviceDefault" />
+    <style name="systemui_theme" parent="@android:style/Theme.DeviceDefault">
+        <item name="android:colorPrimary">@color/primary_color</item>
+    </style>
 
+    <style name="NotificationsQuickSettings">
+        <item name="android:layout_width">match_parent</item>
+        <item name="android:layout_height">match_parent</item>
+    </style>
+
+    <style name="StatusBarHeader">
+        <item name="android:layout_width">match_parent</item>
+    </style>
 </resources>
diff --git a/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java b/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java
index 3ef8316..19a1b11 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java
@@ -255,15 +255,10 @@
     /** Loads the first task thumbnail */
     Bitmap loadFirstTaskThumbnail() {
         SystemServicesProxy ssp = mSystemServicesProxy;
-        List<ActivityManager.RecentTaskInfo> tasks = ssp.getRecentTasks(1,
-                UserHandle.CURRENT.getIdentifier());
-        for (ActivityManager.RecentTaskInfo t : tasks) {
-            // Skip tasks in the home stack
-            if (ssp.isInHomeStack(t.persistentId)) {
-                return null;
-            }
+        List<ActivityManager.RunningTaskInfo> tasks = ssp.getRunningTasks(1);
 
-            return ssp.getTaskThumbnail(t.persistentId);
+        for (ActivityManager.RunningTaskInfo t : tasks) {
+            return ssp.getTaskThumbnail(t.id);
         }
         return null;
     }
@@ -286,17 +281,6 @@
         return (tasks.size() > 1);
     }
 
-    /** Returns whether the base intent of the top task stack was launched with the flag
-     * Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS. */
-    boolean isTopTaskExcludeFromRecents(List<ActivityManager.RecentTaskInfo> tasks) {
-        if (tasks.size() > 0) {
-            ActivityManager.RecentTaskInfo t = tasks.get(0);
-            Console.log(t.baseIntent.toString());
-            return (t.baseIntent.getFlags() & Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) != 0;
-        }
-        return false;
-    }
-
     /** Converts from the device rotation to the degree */
     float getDegreesForRotation(int value) {
         switch (value) {
@@ -416,16 +400,14 @@
         }
 
         // Otherwise, Recents is not the front-most activity and we should animate into it.  If
-        // the activity at the root of the top task stack is excluded from recents, or if that
-        // task stack is in the home stack, then we just do a simple transition.  Otherwise, we
-        // animate to the rects defined by the Recents service, which can differ depending on the
-        // number of items in the list.
+        // the activity at the root of the top task stack in the home stack, then we just do a
+        // simple transition.  Otherwise, we animate to the rects defined by the Recents service,
+        // which can differ depending on the number of items in the list.
         List<ActivityManager.RecentTaskInfo> recentTasks =
-                ssp.getRecentTasks(4, UserHandle.CURRENT.getIdentifier());
+                ssp.getRecentTasks(2, UserHandle.CURRENT.getIdentifier());
         Rect taskRect = hasMultipleRecentsTask(recentTasks) ? mMultipleCountFirstTaskRect :
                 mSingleCountFirstTaskRect;
-        boolean isTaskExcludedFromRecents = isTopTaskExcludeFromRecents(recentTasks);
-        boolean useThumbnailTransition = !isTopTaskHome && !isTaskExcludedFromRecents &&
+        boolean useThumbnailTransition = !isTopTaskHome &&
                 hasValidTaskRects();
 
         if (useThumbnailTransition) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/BakedBezierInterpolator.java b/packages/SystemUI/src/com/android/systemui/recents/BakedBezierInterpolator.java
deleted file mode 100644
index 95ab8e8..0000000
--- a/packages/SystemUI/src/com/android/systemui/recents/BakedBezierInterpolator.java
+++ /dev/null
@@ -1,64 +0,0 @@
-package com.android.systemui.recents;
-
-import android.animation.TimeInterpolator;
-
-/**
- * A pre-baked bezier-curved interpolator for quantum-paper transitions.
- */
-public class BakedBezierInterpolator implements TimeInterpolator {
-    public static final BakedBezierInterpolator INSTANCE = new BakedBezierInterpolator();
-
-    /**
-     * Use the INSTANCE variable instead of instantiating.
-     */
-    private BakedBezierInterpolator() {
-        super();
-    }
-
-    /**
-     * Lookup table values.
-     * Generated using a Bezier curve from (0,0) to (1,1) with control points:
-     * P0 (0,0)
-     * P1 (0.4, 0)
-     * P2 (0.2, 1.0)
-     * P3 (1.0, 1.0)
-     *
-     * Values sampled with x at regular intervals between 0 and 1.
-     */
-    private static final float[] VALUES = new float[] {
-        0.0f, 0.0002f, 0.0009f, 0.0019f, 0.0036f, 0.0059f, 0.0086f, 0.0119f, 0.0157f, 0.0209f,
-        0.0257f, 0.0321f, 0.0392f, 0.0469f, 0.0566f, 0.0656f, 0.0768f, 0.0887f, 0.1033f, 0.1186f,
-        0.1349f, 0.1519f, 0.1696f, 0.1928f, 0.2121f, 0.237f, 0.2627f, 0.2892f, 0.3109f, 0.3386f,
-        0.3667f, 0.3952f, 0.4241f, 0.4474f, 0.4766f, 0.5f, 0.5234f, 0.5468f, 0.5701f, 0.5933f,
-        0.6134f, 0.6333f, 0.6531f, 0.6698f, 0.6891f, 0.7054f, 0.7214f, 0.7346f, 0.7502f, 0.763f,
-        0.7756f, 0.7879f, 0.8f, 0.8107f, 0.8212f, 0.8326f, 0.8415f, 0.8503f, 0.8588f, 0.8672f,
-        0.8754f, 0.8833f, 0.8911f, 0.8977f, 0.9041f, 0.9113f, 0.9165f, 0.9232f, 0.9281f, 0.9328f,
-        0.9382f, 0.9434f, 0.9476f, 0.9518f, 0.9557f, 0.9596f, 0.9632f, 0.9662f, 0.9695f, 0.9722f,
-        0.9753f, 0.9777f, 0.9805f, 0.9826f, 0.9847f, 0.9866f, 0.9884f, 0.9901f, 0.9917f, 0.9931f,
-        0.9944f, 0.9955f, 0.9964f, 0.9973f, 0.9981f, 0.9986f, 0.9992f, 0.9995f, 0.9998f, 1.0f, 1.0f
-    };
-
-    private static final float STEP_SIZE = 1.0f / (VALUES.length - 1);
-
-    @Override
-    public float getInterpolation(float input) {
-        if (input >= 1.0f) {
-            return 1.0f;
-        }
-
-        if (input <= 0f) {
-            return 0f;
-        }
-
-        int position = Math.min(
-                (int)(input * (VALUES.length - 1)),
-                VALUES.length - 2);
-
-        float quantized = position * STEP_SIZE;
-        float difference = input - quantized;
-        float weight = difference / STEP_SIZE;
-
-        return VALUES[position] + weight * (VALUES[position + 1] - VALUES[position]);
-    }
-
-}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Constants.java b/packages/SystemUI/src/com/android/systemui/recents/Constants.java
index 1d6a76c..90998da 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Constants.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Constants.java
@@ -32,7 +32,7 @@
             // Enables the use of theme colors as the task bar background
             public static final boolean EnableTaskBarThemeColors = true;
             // Enables the info pane on long-press
-            public static final boolean EnableInfoPane = true;
+            public static final boolean EnableInfoPane = false;
             // Enables the search bar layout
             public static final boolean EnableSearchLayout = true;
             // Enables the dynamic shadows behind each task
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
index 463cf74..9afc1cb 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
@@ -23,6 +23,8 @@
 import android.graphics.Rect;
 import android.util.DisplayMetrics;
 import android.util.TypedValue;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
 import com.android.systemui.R;
 
 
@@ -42,6 +44,8 @@
 
     public float animationPxMovementPerSecond;
 
+    public Interpolator defaultBezierInterpolator;
+
     public int filteringCurrentViewsMinAnimDuration;
     public int filteringNewViewsMinAnimDuration;
     public int taskBarEnterAnimDuration;
@@ -121,7 +125,6 @@
                 res.getDimensionPixelSize(R.dimen.recents_task_view_z_increment);
         searchBarSpaceHeightPx = res.getDimensionPixelSize(R.dimen.recents_search_bar_space_height);
 
-
         taskBarViewDefaultBackgroundColor =
                 res.getColor(R.color.recents_task_bar_default_background_color);
         taskBarViewDefaultTextColor =
@@ -131,6 +134,9 @@
         taskBarViewDarkTextColor =
                 res.getColor(R.color.recents_task_bar_dark_text_color);
 
+        defaultBezierInterpolator = AnimationUtils.loadInterpolator(context,
+                        com.android.internal.R.interpolator.fast_out_slow_in);
+
         // Update the search widget id
         SharedPreferences settings = context.getSharedPreferences(context.getPackageName(), 0);
         searchBarAppWidgetId = settings.getInt(Constants.Values.App.Key_SearchAppWidgetId, -1);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java
index c64ca54..1ca0476 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java
@@ -35,7 +35,6 @@
 
 import java.util.ArrayList;
 import java.util.Collections;
-import java.util.Iterator;
 import java.util.List;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentLinkedQueue;
@@ -362,7 +361,7 @@
         return mSystemServicesProxy;
     }
 
-    private List<ActivityManager.RecentTaskInfo> getRecentTasks(Context context) {
+    private List<ActivityManager.RecentTaskInfo> getRecentTasks() {
         long t1 = System.currentTimeMillis();
 
         SystemServicesProxy ssp = mSystemServicesProxy;
@@ -375,23 +374,6 @@
         Console.log(Constants.Log.App.TaskDataLoader,
                 "[RecentsTaskLoader|tasks]", "" + tasks.size());
 
-        // Remove home/recents tasks
-        Iterator<ActivityManager.RecentTaskInfo> iter = tasks.iterator();
-        while (iter.hasNext()) {
-            ActivityManager.RecentTaskInfo t = iter.next();
-
-            // Skip tasks in the home stack
-            if (ssp.isInHomeStack(t.persistentId)) {
-                iter.remove();
-                continue;
-            }
-            // Skip tasks from this Recents package
-            if (t.baseIntent.getComponent().getPackageName().equals(context.getPackageName())) {
-                iter.remove();
-                continue;
-            }
-        }
-
         return tasks;
     }
 
@@ -408,7 +390,7 @@
 
         // Get the recent tasks
         SystemServicesProxy ssp = mSystemServicesProxy;
-        List<ActivityManager.RecentTaskInfo> tasks = getRecentTasks(context);
+        List<ActivityManager.RecentTaskInfo> tasks = getRecentTasks();
 
         // Add each task to the task stack
         t1 = System.currentTimeMillis();
diff --git a/packages/SystemUI/src/com/android/systemui/recents/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/SystemServicesProxy.java
index 0d3ee38..8d82883 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/SystemServicesProxy.java
@@ -30,7 +30,6 @@
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageManager;
 import android.graphics.Bitmap;
-import android.graphics.Rect;
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
 import android.os.Bundle;
@@ -40,6 +39,7 @@
 import android.util.Pair;
 
 import java.util.ArrayList;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Random;
 
@@ -54,7 +54,7 @@
     IPackageManager mIpm;
     UserManager mUm;
     SearchManager mSm;
-    String mPackage;
+    String mRecentsPackage;
     ComponentName mAssistComponent;
 
     Bitmap mDummyIcon;
@@ -67,7 +67,7 @@
         mUm = (UserManager) context.getSystemService(Context.USER_SERVICE);
         mIpm = AppGlobals.getPackageManager();
         mSm = (SearchManager) context.getSystemService(Context.SEARCH_SERVICE);
-        mPackage = context.getPackageName();
+        mRecentsPackage = context.getPackageName();
 
         // Resolve the assist intent
         Intent assist = mSm.getAssistIntent(context, false);
@@ -83,14 +83,14 @@
     }
 
     /** Returns a list of the recents tasks */
-    public List<ActivityManager.RecentTaskInfo> getRecentTasks(int numTasks, int userId) {
+    public List<ActivityManager.RecentTaskInfo> getRecentTasks(int numLatestTasks, int userId) {
         if (mAm == null) return null;
 
         // If we are mocking, then create some recent tasks
         if (Constants.DebugFlags.App.EnableSystemServicesProxy) {
             ArrayList<ActivityManager.RecentTaskInfo> tasks =
                     new ArrayList<ActivityManager.RecentTaskInfo>();
-            int count = Math.min(numTasks, Constants.DebugFlags.App.SystemServicesProxyMockTaskCount);
+            int count = Math.min(numLatestTasks, Constants.DebugFlags.App.SystemServicesProxyMockTaskCount);
             for (int i = 0; i < count; i++) {
                 // Create a dummy component name
                 int packageIndex = i % Constants.DebugFlags.App.SystemServicesProxyMockPackageCount;
@@ -114,9 +114,43 @@
             return tasks;
         }
 
-        return mAm.getRecentTasksForUser(numTasks,
+        // Remove home/recents/excluded tasks
+        int minNumTasksToQuery = 10;
+        int numTasksToQuery = Math.max(minNumTasksToQuery, numLatestTasks);
+        List<ActivityManager.RecentTaskInfo> tasks = mAm.getRecentTasksForUser(numTasksToQuery,
                 ActivityManager.RECENT_IGNORE_UNAVAILABLE |
-                        ActivityManager.RECENT_INCLUDE_PROFILES, userId);
+                ActivityManager.RECENT_INCLUDE_PROFILES |
+                ActivityManager.RECENT_WITH_EXCLUDED, userId);
+        boolean isFirstValidTask = true;
+        Iterator<ActivityManager.RecentTaskInfo> iter = tasks.iterator();
+        while (iter.hasNext()) {
+            ActivityManager.RecentTaskInfo t = iter.next();
+
+            // NOTE: The order of these checks happens in the expected order of the traversal of the
+            // tasks
+
+            // Skip tasks from this Recents package
+            if (t.baseIntent.getComponent().getPackageName().equals(mRecentsPackage)) {
+                iter.remove();
+                continue;
+            }
+            // Check the first non-recents task, include this task even if it is marked as excluded
+            // from recents.  In other words, only remove excluded tasks if it is not the first task
+            boolean isExcluded = (t.baseIntent.getFlags() & Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
+                    == Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
+            if (isExcluded && !isFirstValidTask) {
+                iter.remove();
+                continue;
+            }
+            isFirstValidTask = false;
+            // Skip tasks in the home stack
+            if (isInHomeStack(t.persistentId)) {
+                iter.remove();
+                continue;
+            }
+        }
+
+        return tasks.subList(0, Math.min(tasks.size(), numLatestTasks));
     }
 
     /** Returns a list of the running tasks */
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Utilities.java b/packages/SystemUI/src/com/android/systemui/recents/Utilities.java
index b602f84..46e6ee9 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Utilities.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Utilities.java
@@ -18,6 +18,7 @@
 
 import android.graphics.Color;
 import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
 
 /* Common code */
 public class Utilities {
@@ -54,12 +55,15 @@
                 0.0722f * Color.blue(color));
     }
 
-    /** Returns the ideal text color to draw on top of a specified background color. */
-    public static int getIdealTextColorForBackgroundColor(int color) {
-        RecentsConfiguration configuration = RecentsConfiguration.getInstance();
+    /** Returns the ideal color to draw on top of a specified background color. */
+    public static int getIdealColorForBackgroundColor(int color, int lightRes, int darkRes) {
         int greyscale = colorToGreyscale(color);
-        return (greyscale < 128) ? configuration.taskBarViewLightTextColor :
-                configuration.taskBarViewDarkTextColor;
-
+        return (greyscale < 128) ? lightRes : darkRes;
+    }
+    /** Returns the ideal drawable to draw on top of a specified background color. */
+    public static Drawable getIdealResourceForBackgroundColor(int color, Drawable lightRes,
+                                                           Drawable darkRes) {
+        int greyscale = colorToGreyscale(color);
+        return (greyscale < 128) ? lightRes : darkRes;
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java
index c6cb812..07caa1b 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java
@@ -16,7 +16,10 @@
 
 package com.android.systemui.recents.views;
 
+import android.animation.ValueAnimator;
 import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
 import android.util.AttributeSet;
 import android.widget.FrameLayout;
 import android.widget.ImageView;
@@ -32,9 +35,13 @@
 class TaskBarView extends FrameLayout {
     Task mTask;
 
+    ImageView mDismissButton;
     ImageView mApplicationIcon;
     TextView mActivityDescription;
 
+    Drawable mLightDismissDrawable;
+    Drawable mDarkDismissDrawable;
+
     public TaskBarView(Context context) {
         this(context, null);
     }
@@ -49,6 +56,9 @@
 
     public TaskBarView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
         super(context, attrs, defStyleAttr, defStyleRes);
+        Resources res = context.getResources();
+        mLightDismissDrawable = res.getDrawable(R.drawable.recents_dismiss_light);
+        mDarkDismissDrawable = res.getDrawable(R.drawable.recents_dismiss_dark);
     }
 
     @Override
@@ -56,6 +66,28 @@
         // Initialize the icon and description views
         mApplicationIcon = (ImageView) findViewById(R.id.application_icon);
         mActivityDescription = (TextView) findViewById(R.id.activity_description);
+        mDismissButton = (ImageView) findViewById(R.id.dismiss_task);
+    }
+
+    /** Synchronizes this bar view's properties with the task's transform */
+    void updateViewPropertiesToTaskTransform(TaskViewTransform animateFromTransform,
+                                             TaskViewTransform toTransform, int duration) {
+        RecentsConfiguration config = RecentsConfiguration.getInstance();
+        if (duration > 0) {
+            if (animateFromTransform != null) {
+                mDismissButton.setAlpha(animateFromTransform.dismissAlpha);
+            }
+            mDismissButton.animate()
+                    .alpha(toTransform.dismissAlpha)
+                    .setStartDelay(0)
+                    .setDuration(duration)
+                    .setInterpolator(config.defaultBezierInterpolator)
+                    .withLayer()
+                    .start();
+        } else {
+            mDismissButton.setAlpha(toTransform.dismissAlpha);
+        }
+        mDismissButton.invalidate();
     }
 
     /** Binds the bar view to the task */
@@ -74,7 +106,10 @@
         int tint = t.colorPrimary;
         if (Constants.DebugFlags.App.EnableTaskBarThemeColors && tint != 0) {
             setBackgroundColor(tint);
-            mActivityDescription.setTextColor(Utilities.getIdealTextColorForBackgroundColor(tint));
+            mActivityDescription.setTextColor(Utilities.getIdealColorForBackgroundColor(tint,
+                    configuration.taskBarViewLightTextColor, configuration.taskBarViewDarkTextColor));
+            mDismissButton.setImageDrawable(Utilities.getIdealResourceForBackgroundColor(tint,
+                    mLightDismissDrawable, mDarkDismissDrawable));
         } else {
             setBackgroundColor(configuration.taskBarViewDefaultBackgroundColor);
             mActivityDescription.setTextColor(configuration.taskBarViewDefaultTextColor);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskInfoView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskInfoView.java
index c6c29a6..f1c362a 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskInfoView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskInfoView.java
@@ -30,7 +30,6 @@
 import android.widget.Button;
 import android.widget.FrameLayout;
 import com.android.systemui.R;
-import com.android.systemui.recents.BakedBezierInterpolator;
 import com.android.systemui.recents.Constants;
 import com.android.systemui.recents.RecentsConfiguration;
 import com.android.systemui.recents.Utilities;
@@ -111,7 +110,8 @@
         int duration = Utilities.calculateTranslationAnimationDuration((int) mMaxClipRadius);
         mCircularClipAnimator = ObjectAnimator.ofFloat(this, "circularClipRadius", toRadius);
         mCircularClipAnimator.setDuration(duration);
-        mCircularClipAnimator.setInterpolator(BakedBezierInterpolator.INSTANCE);
+        mCircularClipAnimator.setInterpolator(
+                RecentsConfiguration.getInstance().defaultBezierInterpolator);
         mCircularClipAnimator.addListener(new AnimatorListenerAdapter() {
             @Override
             public void onAnimationEnd(Animator animation) {
@@ -143,7 +143,7 @@
                 .scaleX(1f)
                 .scaleY(1f)
                 .setDuration(duration)
-                .setInterpolator(BakedBezierInterpolator.INSTANCE)
+                .setInterpolator(RecentsConfiguration.getInstance().defaultBezierInterpolator)
                 .withLayer()
                 .start();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
index 55c38a9..b64225e 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -37,7 +37,6 @@
 import android.widget.FrameLayout;
 import android.widget.OverScroller;
 import com.android.systemui.R;
-import com.android.systemui.recents.BakedBezierInterpolator;
 import com.android.systemui.recents.Console;
 import com.android.systemui.recents.Constants;
 import com.android.systemui.recents.RecentsConfiguration;
@@ -170,6 +169,9 @@
             transform.translationY = (int) (boundedT * overlapHeight - scaleYOffset);
         }
 
+        // Set the alphas
+        transform.dismissAlpha = Math.max(-1f, Math.min(0f, t)) + 1f;
+
         // Update the rect and visibility
         transform.rect.set(mTaskRect);
         if (t < -(numPeekCards + 1)) {
@@ -338,7 +340,7 @@
         mScrollAnimator = ObjectAnimator.ofInt(this, "stackScroll", curScroll, newScroll);
         mScrollAnimator.setDuration(Utilities.calculateTranslationAnimationDuration(newScroll -
                 curScroll, 250));
-        mScrollAnimator.setInterpolator(BakedBezierInterpolator.INSTANCE);
+        mScrollAnimator.setInterpolator(RecentsConfiguration.getInstance().defaultBezierInterpolator);
         mScrollAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
             @Override
             public void onAnimationUpdate(ValueAnimator animation) {
@@ -1036,6 +1038,15 @@
         }
     }
 
+    @Override
+    public void onTaskDismissed(TaskView tv) {
+        Task task = tv.getTask();
+        // Remove the task from the view
+        mStack.removeTask(task);
+        // Notify the callback that we've removed the task and it can clean up after it
+        mCb.onTaskRemoved(task);
+    }
+
     /**** View.OnClickListener Implementation ****/
 
     @Override
@@ -1095,6 +1106,7 @@
 
     @Override
     public void onComponentRemoved(Set<ComponentName> cns) {
+        RecentsConfiguration config = RecentsConfiguration.getInstance();
         // For other tasks, just remove them directly if they no longer exist
         ArrayList<Task> tasks = mStack.getTasks();
         for (int i = tasks.size() - 1; i >= 0; i--) {
@@ -1477,13 +1489,7 @@
     @Override
     public void onChildDismissed(View v) {
         TaskView tv = (TaskView) v;
-        Task task = tv.getTask();
-
-        // Remove the task from the view
-        mSv.mStack.removeTask(task);
-
-        // Notify the callback that we've removed the task and it can clean up after it
-        mSv.mCb.onTaskRemoved(task);
+        mSv.onTaskDismissed(tv);
 
         // Disable HW layers
         mSv.decHwLayersRefCount("swipeComplete");
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
index b03f389..5fad629 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
@@ -31,7 +31,6 @@
 import android.view.animation.AccelerateInterpolator;
 import android.widget.FrameLayout;
 import com.android.systemui.R;
-import com.android.systemui.recents.BakedBezierInterpolator;
 import com.android.systemui.recents.Constants;
 import com.android.systemui.recents.RecentsConfiguration;
 import com.android.systemui.recents.model.Task;
@@ -46,6 +45,7 @@
         public void onTaskInfoPanelShown(TaskView tv);
         public void onTaskInfoPanelHidden(TaskView tv);
         public void onTaskAppInfoClicked(TaskView tv);
+        public void onTaskDismissed(TaskView tv);
 
         // public void onTaskViewReboundToTask(TaskView tv, Task t);
     }
@@ -143,6 +143,10 @@
         int minZ = config.taskViewTranslationZMinPx;
         int incZ = config.taskViewTranslationZIncrementPx;
 
+        // Update the bar view
+        mBarView.updateViewPropertiesToTaskTransform(animateFromTransform, toTransform, duration);
+
+        // Update this task view
         if (duration > 0) {
             if (animateFromTransform != null) {
                 setTranslationY(animateFromTransform.translationY);
@@ -161,7 +165,7 @@
                     .scaleY(toTransform.scale)
                     .alpha(toTransform.alpha)
                     .setDuration(duration)
-                    .setInterpolator(BakedBezierInterpolator.INSTANCE)
+                    .setInterpolator(config.defaultBezierInterpolator)
                     .withLayer()
                     .setUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                         @Override
@@ -221,8 +225,8 @@
         mBarView.setAlpha(0f);
         mBarView.animate()
                 .alpha(1f)
-                .setStartDelay(235)
-                .setInterpolator(BakedBezierInterpolator.INSTANCE)
+                .setStartDelay(250)
+                .setInterpolator(config.defaultBezierInterpolator)
                 .setDuration(config.taskBarEnterAnimDuration)
                 .withLayer()
                 .start();
@@ -234,7 +238,7 @@
         mBarView.animate()
             .alpha(0f)
             .setStartDelay(0)
-            .setInterpolator(BakedBezierInterpolator.INSTANCE)
+            .setInterpolator(config.defaultBezierInterpolator)
             .setDuration(config.taskBarExitAnimDuration)
             .withLayer()
             .withEndAction(new Runnable() {
@@ -252,7 +256,7 @@
         animate().translationX(config.taskViewRemoveAnimTranslationXPx)
             .alpha(0f)
             .setStartDelay(0)
-            .setInterpolator(BakedBezierInterpolator.INSTANCE)
+            .setInterpolator(config.defaultBezierInterpolator)
             .setDuration(config.taskViewRemoveAnimDuration)
             .withLayer()
             .withEndAction(new Runnable() {
@@ -310,7 +314,7 @@
         mInfoView.animate()
                 .alpha(0f)
                 .setDuration(config.taskViewInfoPaneAnimDuration)
-                .setInterpolator(BakedBezierInterpolator.INSTANCE)
+                .setInterpolator(config.defaultBezierInterpolator)
                 .withLayer()
                 .withEndAction(new Runnable() {
                     @Override
@@ -380,6 +384,7 @@
             mInfoView.rebindToTask(mTask, reloadingTaskData);
             // Rebind any listeners
             mBarView.mApplicationIcon.setOnClickListener(this);
+            mBarView.mDismissButton.setOnClickListener(this);
             mInfoView.mAppInfoButton.setOnClickListener(this);
         }
         mTaskDataLoaded = true;
@@ -405,6 +410,15 @@
             hideInfoPane();
         } else if (v == mBarView.mApplicationIcon) {
             mCb.onTaskIconClicked(this);
+        } else if (v == mBarView.mDismissButton) {
+            // Animate out the view and call the callback
+            final TaskView tv = this;
+            animateRemoval(new Runnable() {
+                @Override
+                public void run() {
+                    mCb.onTaskDismissed(tv);
+                }
+            });
         } else if (v == mInfoView.mAppInfoButton) {
             mCb.onTaskAppInfoClicked(this);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java
index 0748bbb..e6391a8 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java
@@ -24,6 +24,7 @@
     public int translationY = 0;
     public float scale = 1f;
     public float alpha = 1f;
+    public float dismissAlpha = 1f;
     public boolean visible = false;
     public Rect rect = new Rect();
     float t;
@@ -36,6 +37,7 @@
         translationY = o.translationY;
         scale = o.scale;
         alpha = o.alpha;
+        dismissAlpha = o.dismissAlpha;
         visible = o.visible;
         rect.set(o.rect);
         t = o.t;
@@ -44,6 +46,6 @@
     @Override
     public String toString() {
         return "TaskViewTransform y: " + translationY + " scale: " + scale + " alpha: " + alpha +
-                " visible: " + visible + " rect: " + rect;
+                " visible: " + visible + " rect: " + rect + " dismissAlpha: " + dismissAlpha;
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java b/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java
index 327e715..1747e6e 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java
@@ -119,7 +119,6 @@
             }
         };
         mBrightnessObserver = new BrightnessObserver(mHandler);
-        mBrightnessObserver.startObserving();
 
         PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
         mMinimumBacklight = pm.getMinimumScreenBrightnessSetting();
@@ -128,13 +127,6 @@
         mAutomaticAvailable = context.getResources().getBoolean(
                 com.android.internal.R.bool.config_automatic_brightness_available);
         mPower = IPowerManager.Stub.asInterface(ServiceManager.getService("power"));
-
-        // Update the slider and mode before attaching the listener so we don't receive the
-        // onChanged notifications for the initial values.
-        updateMode();
-        updateSlider();
-
-        control.setOnChangedListener(this);
     }
 
     public void addStateChangedCallback(BrightnessStateChangeCallback cb) {
@@ -150,11 +142,24 @@
         // Do nothing
     }
 
+    public void registerCallbacks() {
+        mBrightnessObserver.startObserving();
+        mUserTracker.startTracking();
+
+        // Update the slider and mode before attaching the listener so we don't receive the
+        // onChanged notifications for the initial values.
+        updateMode();
+        updateSlider();
+
+        mControl.setOnChangedListener(this);
+    }
+
     /** Unregister all call backs, both to and from the controller */
     public void unregisterCallbacks() {
         mBrightnessObserver.stopObserving();
         mChangeCallbacks.clear();
         mUserTracker.stopTracking();
+        mControl.setOnChangedListener(null);
     }
 
     public void onChanged(ToggleSlider view, boolean tracking, boolean automatic, int value) {
diff --git a/packages/SystemUI/src/com/android/systemui/settings/BrightnessDialog.java b/packages/SystemUI/src/com/android/systemui/settings/BrightnessDialog.java
index bd5e5e8..27881c4 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/BrightnessDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/BrightnessDialog.java
@@ -92,6 +92,7 @@
         mBrightnessController = new BrightnessController(getContext(),
                 (ImageView) findViewById(R.id.brightness_icon),
                 (ToggleSlider) findViewById(R.id.brightness_slider));
+        mBrightnessController.registerCallbacks();
         dismissBrightnessDialog(mBrightnessDialogLongTimeout);
         mBrightnessController.addStateChangedCallback(this);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/settings/CurrentUserTracker.java b/packages/SystemUI/src/com/android/systemui/settings/CurrentUserTracker.java
index 036bd4f..f8ff616 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/CurrentUserTracker.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/CurrentUserTracker.java
@@ -29,9 +29,6 @@
     private int mCurrentUserId;
 
     public CurrentUserTracker(Context context) {
-        IntentFilter filter = new IntentFilter(Intent.ACTION_USER_SWITCHED);
-        context.registerReceiver(this, filter);
-        mCurrentUserId = ActivityManager.getCurrentUser();
         mContext = context;
     }
 
@@ -50,6 +47,12 @@
         }
     }
 
+    public void startTracking() {
+        mCurrentUserId = ActivityManager.getCurrentUser();
+        IntentFilter filter = new IntentFilter(Intent.ACTION_USER_SWITCHED);
+        mContext.registerReceiver(this, filter);
+    }
+
     public void stopTracking() {
         mContext.unregisterReceiver(this);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/settings/ToggleSlider.java b/packages/SystemUI/src/com/android/systemui/settings/ToggleSlider.java
index 7d38058..4b78072 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/ToggleSlider.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/ToggleSlider.java
@@ -62,7 +62,6 @@
 
         mToggle = (CompoundButton) findViewById(R.id.toggle);
         mToggle.setOnCheckedChangeListener(mCheckListener);
-        mToggle.setBackground(res.getDrawable(R.drawable.status_bar_toggle_button));
 
         mSlider = (SeekBar) findViewById(R.id.slider);
         mSlider.setOnSeekBarChangeListener(mSeekListener);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
index 281bd2d..4bd0e1c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
@@ -112,7 +112,7 @@
      * @return The desired notification height.
      */
     public int getIntrinsicHeight() {
-        return mActualHeight;
+        return getHeight();
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/FlingAnimationUtils.java b/packages/SystemUI/src/com/android/systemui/statusbar/FlingAnimationUtils.java
new file mode 100644
index 0000000..0606a94
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/FlingAnimationUtils.java
@@ -0,0 +1,129 @@
+/*
+ * 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.animation.ValueAnimator;
+import android.content.Context;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
+import android.view.animation.PathInterpolator;
+
+/**
+ * Utility class to calculate general fling animation when the finger is released.
+ */
+public class FlingAnimationUtils {
+
+    private static final float LINEAR_OUT_SLOW_IN_Y2 = 0.35f;
+    private static final float MAX_LENGTH_SECONDS = 0.4f;
+    private static final float MIN_VELOCITY_DP_PER_SECOND = 250;
+
+    /**
+     * Crazy math. http://en.wikipedia.org/wiki/B%C3%A9zier_curve
+     */
+    private static final float LINEAR_OUT_SLOW_IN_START_GRADIENT = 1/LINEAR_OUT_SLOW_IN_Y2;
+
+    private Interpolator mLinearOutSlowIn;
+    private Interpolator mFastOutSlowIn;
+    private float mMinVelocityPxPerSecond;
+
+    public FlingAnimationUtils(Context ctx) {
+        mLinearOutSlowIn = new PathInterpolator(0, 0, LINEAR_OUT_SLOW_IN_Y2, 1);
+        mFastOutSlowIn
+                = AnimationUtils.loadInterpolator(ctx, android.R.interpolator.fast_out_slow_in);
+        mMinVelocityPxPerSecond
+                = MIN_VELOCITY_DP_PER_SECOND * ctx.getResources().getDisplayMetrics().density;
+    }
+
+    /**
+     * 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
+     */
+    public void apply(ValueAnimator animator, float currValue, float endValue, float velocity) {
+        float diff = Math.abs(endValue - currValue);
+        float velAbs = Math.abs(velocity);
+        float durationSeconds = LINEAR_OUT_SLOW_IN_START_GRADIENT * diff / velAbs;
+        if (durationSeconds <= MAX_LENGTH_SECONDS) {
+            animator.setInterpolator(mLinearOutSlowIn);
+        } else if (velAbs >= mMinVelocityPxPerSecond) {
+
+            // Cross fade between fast-out-slow-in and linear interpolator with current velocity.
+            durationSeconds = MAX_LENGTH_SECONDS;
+            VelocityInterpolator velocityInterpolator
+                    = new VelocityInterpolator(durationSeconds, velAbs, diff);
+            InterpolatorInterpolator superInterpolator = new InterpolatorInterpolator(
+                    velocityInterpolator, mLinearOutSlowIn, mLinearOutSlowIn);
+            animator.setInterpolator(superInterpolator);
+        } else {
+
+            // Just use a normal interpolator which doesn't take the velocity into account.
+            durationSeconds = MAX_LENGTH_SECONDS;
+            animator.setInterpolator(mFastOutSlowIn);
+        }
+        animator.setDuration((long) (durationSeconds * 1000));
+    }
+
+    /**
+     * An interpolator which interpolates two interpolators with an interpolator.
+     */
+    private static final class InterpolatorInterpolator implements Interpolator {
+
+        private Interpolator mInterpolator1;
+        private Interpolator mInterpolator2;
+        private Interpolator mCrossfader;
+
+        InterpolatorInterpolator(Interpolator interpolator1, Interpolator interpolator2,
+                Interpolator crossfader) {
+            mInterpolator1 = interpolator1;
+            mInterpolator2 = interpolator2;
+            mCrossfader = crossfader;
+        }
+
+        @Override
+        public float getInterpolation(float input) {
+            float t = mCrossfader.getInterpolation(input);
+            return (1 - t) * mInterpolator1.getInterpolation(input)
+                    + t * mInterpolator2.getInterpolation(input);
+        }
+    }
+
+    /**
+     * An interpolator which interpolates with a fixed velocity.
+     */
+    private static final class VelocityInterpolator implements Interpolator {
+
+        private float mDurationSeconds;
+        private float mVelocity;
+        private float mDiff;
+
+        private VelocityInterpolator(float durationSeconds, float velocity, float diff) {
+            mDurationSeconds = durationSeconds;
+            mVelocity = velocity;
+            mDiff = diff;
+        }
+
+        @Override
+        public float getInterpolation(float input) {
+            float time = input * mDurationSeconds;
+            return time * mVelocity / mDiff;
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationOverflowContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationOverflowContainer.java
index 864c597..451c5c5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationOverflowContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationOverflowContainer.java
@@ -34,21 +34,6 @@
     }
 
     @Override
-    public void setActualHeight(int currentHeight, boolean notifyListeners) {
-        // noop
-    }
-
-    @Override
-    public int getActualHeight() {
-        return getHeight();
-    }
-
-    @Override
-    public void setClipTopAmount(int clipTopAmount) {
-        // noop
-    }
-
-    @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
         mIconsView = (NotificationOverflowIconsView) findViewById(R.id.overflow_icons_view);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java
new file mode 100644
index 0000000..c26f15e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java
@@ -0,0 +1,90 @@
+/*
+ * 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.Intent;
+import android.graphics.Canvas;
+import android.graphics.Path;
+import android.graphics.drawable.Drawable;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.provider.ContactsContract;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageButton;
+
+import com.android.systemui.R;
+import com.android.systemui.settings.UserSwitcherHostView;
+import com.android.systemui.statusbar.policy.UserInfoController;
+
+/**
+ * Image button for the multi user switcher.
+ */
+public class MultiUserSwitch extends ImageButton implements View.OnClickListener,
+        UserInfoController.OnUserInfoChangedListener {
+
+    private ViewGroup mOverlayParent;
+
+    public MultiUserSwitch(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        setOnClickListener(this);
+    }
+
+    public void setOverlayParent(ViewGroup parent) {
+        mOverlayParent = parent;
+    }
+
+    @Override
+    public void onClick(View v) {
+        final UserManager um = UserManager.get(getContext());
+        if (um.isUserSwitcherEnabled()) {
+            final UserSwitcherHostView switcher =
+                    (UserSwitcherHostView) LayoutInflater.from(getContext()).inflate(
+                            R.layout.user_switcher_host, mOverlayParent, false);
+            switcher.setFinishRunnable(new Runnable() {
+                @Override
+                public void run() {
+                    mOverlayParent.removeView(switcher);
+                }
+            });
+            switcher.refreshUsers();
+            mOverlayParent.addView(switcher);
+        } else {
+            Intent intent = ContactsContract.QuickContact.composeQuickContactsIntent(
+                    getContext(), v, ContactsContract.Profile.CONTENT_URI,
+                    ContactsContract.QuickContact.MODE_LARGE, null);
+            getContext().startActivityAsUser(intent, new UserHandle(UserHandle.USER_CURRENT));
+        }
+    }
+
+    public void setUserInfoController(UserInfoController userInfoController) {
+        userInfoController.addListener(this);
+    }
+
+    @Override
+    public void onUserInfoChanged(String name, Drawable picture) {
+        setImageDrawable(picture);
+    }
+}
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 f63ba9c..150db63 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -24,12 +24,15 @@
 import android.view.MotionEvent;
 import android.view.VelocityTracker;
 import android.view.View;
+import android.view.ViewGroup;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.animation.AnimationUtils;
 import android.view.animation.Interpolator;
+import android.widget.LinearLayout;
 
 import com.android.systemui.R;
 import com.android.systemui.statusbar.ExpandableView;
+import com.android.systemui.statusbar.FlingAnimationUtils;
 import com.android.systemui.statusbar.GestureRecorder;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
@@ -51,15 +54,21 @@
     private int mNotificationTopPadding;
     private boolean mAnimateNextTopPaddingChange;
 
-    private Interpolator mExpansionInterpolator;
-
     private int mTrackingPointer;
     private VelocityTracker mVelocityTracker;
     private boolean mTracking;
+
+    /**
+     * Whether we are currently handling a motion gesture in #onInterceptTouchEvent, but haven't
+     * intercepted yet.
+     */
+    private boolean mIntercepting;
     private boolean mQsExpanded;
     private float mInitialHeightOnTouch;
     private float mInitialTouchX;
     private float mInitialTouchY;
+    private float mLastTouchX;
+    private float mLastTouchY;
     private float mQsExpansionHeight;
     private int mQsMinExpansionHeight;
     private int mQsMaxExpansionHeight;
@@ -68,6 +77,7 @@
     private int mStackScrollerIntrinsicPadding;
     private boolean mQsExpansionEnabled = true;
     private ValueAnimator mQsExpansionAnimator;
+    private FlingAnimationUtils mFlingAnimationUtils;
 
     public NotificationPanelView(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -93,6 +103,7 @@
         super.onFinishInflate();
         mHeader = (StatusBarHeaderView) findViewById(R.id.header);
         mHeader.getBackgroundView().setOnClickListener(this);
+        mHeader.setOverlayParent(this);
         mKeyguardStatusView = findViewById(R.id.keyguard_status_view);
         mStackScrollerContainer = findViewById(R.id.notification_container_parent);
         mQsContainer = (QuickSettingsContainerView) findViewById(R.id.quick_settings_container);
@@ -104,8 +115,7 @@
         mNotificationTopPadding = getResources().getDimensionPixelSize(
                 R.dimen.notifications_top_padding);
         mMinStackHeight = getResources().getDimensionPixelSize(R.dimen.collapsed_stack_height);
-        mExpansionInterpolator = AnimationUtils.loadInterpolator(
-                getContext(), android.R.interpolator.fast_out_slow_in);
+        mFlingAnimationUtils = new FlingAnimationUtils(getContext());
     }
 
     @Override
@@ -144,7 +154,6 @@
 
     public void setQsExpansionEnabled(boolean qsExpansionEnabled) {
         mQsExpansionEnabled = qsExpansionEnabled;
-        mHeader.setExpansionEnabled(qsExpansionEnabled);
     }
 
     public void closeQs() {
@@ -193,6 +202,7 @@
 
         switch (event.getActionMasked()) {
             case MotionEvent.ACTION_DOWN:
+                mIntercepting = true;
                 mInitialTouchY = y;
                 mInitialTouchX = x;
                 initVelocityTracker();
@@ -215,6 +225,16 @@
             case MotionEvent.ACTION_MOVE:
                 final float h = y - mInitialTouchY;
                 trackMovement(event);
+                if (mTracking) {
+
+                    // 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
+                    // onTouchEvent.
+                    setQsExpansion(h + mInitialHeightOnTouch);
+                    trackMovement(event);
+                    mIntercepting = false;
+                    return true;
+                }
                 if (Math.abs(h) > mTouchSlop && Math.abs(h) > Math.abs(x - mInitialTouchX)
                         && shouldIntercept(mInitialTouchX, mInitialTouchY, h)) {
                     onQsExpansionStarted();
@@ -222,14 +242,41 @@
                     mInitialTouchY = y;
                     mInitialTouchX = x;
                     mTracking = true;
+                    mIntercepting = false;
                     return true;
                 }
                 break;
+
+            case MotionEvent.ACTION_CANCEL:
+            case MotionEvent.ACTION_UP:
+                trackMovement(event);
+                if (mTracking) {
+                    flingWithCurrentVelocity();
+                    mTracking = false;
+                }
+                mIntercepting = false;
+                break;
         }
         return !mQsExpanded && super.onInterceptTouchEvent(event);
     }
 
     @Override
+    public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
+
+        // Block request so we can still intercept the scrolling when QS is expanded.
+        if (!mQsExpanded) {
+            super.requestDisallowInterceptTouchEvent(disallowIntercept);
+        }
+    }
+
+    private void flingWithCurrentVelocity() {
+        float vel = getCurrentVelocity();
+
+        // TODO: Better logic whether we should expand or not.
+        flingSettings(vel, vel > 0);
+    }
+
+    @Override
     public boolean onTouchEvent(MotionEvent event) {
         // TODO: Handle doublefinger swipe to notifications again. Look at history for a reference
         // implementation.
@@ -278,12 +325,7 @@
                     mTracking = false;
                     mTrackingPointer = -1;
                     trackMovement(event);
-
-                    float vel = getCurrentVelocity();
-
-                    // TODO: Better logic whether we should expand or not.
-                    flingSettings(vel, vel > 0);
-
+                    flingWithCurrentVelocity();
                     if (mVelocityTracker != null) {
                         mVelocityTracker.recycle();
                         mVelocityTracker = null;
@@ -297,11 +339,26 @@
         return mQsExpanded || super.onTouchEvent(event);
     }
 
+    @Override
+    public void onOverscrolled(int amount) {
+        if (mIntercepting) {
+            onQsExpansionStarted(amount);
+            mInitialHeightOnTouch = mQsExpansionHeight;
+            mInitialTouchY = mLastTouchY;
+            mInitialTouchX = mLastTouchX;
+            mTracking = true;
+        }
+    }
+
     private void onQsExpansionStarted() {
+        onQsExpansionStarted(0);
+    }
+
+    private void onQsExpansionStarted(int overscrollAmount) {
         cancelAnimation();
 
         // Reset scroll position and apply that position to the expanded height.
-        float height = mQsExpansionHeight - mScrollView.getScrollY();
+        float height = mQsExpansionHeight - mScrollView.getScrollY() - overscrollAmount;
         mScrollView.scrollTo(0, 0);
         setQsExpansion(height);
     }
@@ -359,6 +416,8 @@
 
     private void trackMovement(MotionEvent event) {
         if (mVelocityTracker != null) mVelocityTracker.addMovement(event);
+        mLastTouchX = event.getX();
+        mLastTouchY = event.getY();
     }
 
     private void initVelocityTracker() {
@@ -382,13 +441,9 @@
         }
     }
     private void flingSettings(float vel, boolean expand) {
-
-        // TODO: Actually use velocity.
-
         float target = expand ? mQsMaxExpansionHeight : mQsMinExpansionHeight;
         ValueAnimator animator = ValueAnimator.ofFloat(mQsExpansionHeight, target);
-        animator.setDuration(EXPANSION_ANIMATION_LENGTH);
-        animator.setInterpolator(mExpansionInterpolator);
+        mFlingAnimationUtils.apply(animator, mQsExpansionHeight, target, vel);
         animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
             @Override
             public void onAnimationUpdate(ValueAnimator animation) {
@@ -412,11 +467,8 @@
         if (!mQsExpansionEnabled) {
             return false;
         }
-        View headerView = mStatusBar.getBarState() == StatusBarState.KEYGUARD && !mQsExpanded
-                ? mKeyguardStatusView
-                : mHeader;
-        boolean onHeader = x >= headerView.getLeft() && x <= headerView.getRight()
-                && y >= headerView.getTop() && y <= headerView.getBottom();
+        boolean onHeader = x >= mHeader.getLeft() && x <= mHeader.getRight()
+                && y >= mHeader.getTop() && y <= mHeader.getBottom();
         if (mQsExpanded) {
             return onHeader || (mScrollView.isScrolledToBottom() && yDiff < 0);
         } else {
@@ -425,6 +477,35 @@
     }
 
     @Override
+    public void setVisibility(int visibility) {
+        int oldVisibility = getVisibility();
+        super.setVisibility(visibility);
+        if (visibility != oldVisibility) {
+            reparentStatusIcons(visibility == VISIBLE);
+        }
+    }
+
+    /**
+     * When the notification panel gets expanded, we need to move the status icons in the header
+     * card.
+     */
+    private void reparentStatusIcons(boolean toHeader) {
+        if (mStatusBar == null) {
+            return;
+        }
+        LinearLayout systemIcons = mStatusBar.getSystemIcons();
+        if (systemIcons.getParent() != null) {
+            ((ViewGroup) systemIcons.getParent()).removeView(systemIcons);
+        }
+        if (toHeader) {
+            mHeader.attachSystemIcons(systemIcons);
+        } else {
+            mHeader.onSystemIconsDetached();
+            mStatusBar.reattachSystemIcons();
+        }
+    }
+
+    @Override
     protected boolean isScrolledToBottom() {
         if (!isInSettings()) {
             return mNotificationStackScroller.isScrolledToBottom();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ObservableScrollView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ObservableScrollView.java
index 46484f3..ba0b66e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ObservableScrollView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ObservableScrollView.java
@@ -27,6 +27,7 @@
 public class ObservableScrollView extends ScrollView {
 
     private Listener mListener;
+    private int mLastOverscrollAmount;
 
     public ObservableScrollView(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -58,7 +59,25 @@
         }
     }
 
+    @Override
+    protected boolean overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY,
+            int scrollRangeX, int scrollRangeY, int maxOverScrollX, int maxOverScrollY,
+            boolean isTouchEvent) {
+        mLastOverscrollAmount = Math.max(0, scrollY + deltaY - getMaxScrollY());
+        return super.overScrollBy(deltaX, deltaY, scrollX, scrollY, scrollRangeX, scrollRangeY,
+                        maxOverScrollX, maxOverScrollY, isTouchEvent);
+    }
+
+    @Override
+    protected void onOverScrolled(int scrollX, int scrollY, boolean clampedX, boolean clampedY) {
+        super.onOverScrolled(scrollX, scrollY, clampedX, clampedY);
+        if (mListener != null && mLastOverscrollAmount > 0) {
+            mListener.onOverscrolled(mLastOverscrollAmount);
+        }
+    }
+
     public interface Listener {
         void onScrollChanged();
+        void onOverscrolled(int amount);
     }
 }
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 92eee4e..4d09d6a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -29,7 +29,6 @@
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
-import android.animation.ObjectAnimator;
 import android.animation.TimeInterpolator;
 import android.app.ActivityManager;
 import android.app.ActivityManagerNative;
@@ -112,6 +111,7 @@
 import com.android.systemui.statusbar.policy.LocationController;
 import com.android.systemui.statusbar.policy.NetworkController;
 import com.android.systemui.statusbar.policy.RotationLockController;
+import com.android.systemui.statusbar.policy.UserInfoController;
 import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
 import com.android.systemui.statusbar.stack.NotificationStackScrollLayout.OnChildLocationsChangedListener;
 import com.android.systemui.statusbar.stack.StackScrollState.ViewState;
@@ -185,6 +185,7 @@
     LocationController mLocationController;
     NetworkController mNetworkController;
     RotationLockController mRotationLockController;
+    UserInfoController mUserInfoController;
 
     int mNaturalBarHeight = -1;
     int mIconSize = -1;
@@ -205,6 +206,7 @@
 
     // right-hand icons
     LinearLayout mSystemIconArea;
+    LinearLayout mSystemIcons;
 
     // left-hand icons
     LinearLayout mStatusIcons;
@@ -221,7 +223,6 @@
     int mNotificationPanelGravity;
     int mNotificationPanelMarginBottomPx;
     float mNotificationPanelMinHeightFrac;
-    boolean mNotificationPanelIsFullScreenWidth;
     TextView mNotificationPanelDebugText;
 
     // settings
@@ -230,7 +231,7 @@
     QuickSettingsContainerView mSettingsContainer;
 
     // top bar
-    View mNotificationPanelHeader;
+    StatusBarHeaderView mHeader;
     View mKeyguardStatusView;
     KeyguardBottomAreaView mKeyguardBottomArea;
     boolean mLeaveOpenOnKeyguardHide;
@@ -240,8 +241,6 @@
     String mKeyguardHotwordPhrase = "";
     int mKeyguardMaxNotificationCount;
     View mDateTimeView;
-    View mClearButton;
-    ImageView mHeaderFlipper;
 
     // carrier/wifi label
     private TextView mCarrierLabel;
@@ -249,7 +248,6 @@
     private int mCarrierLabelHeight;
     private TextView mEmergencyCallLabel;
     private int mStatusBarHeaderHeight;
-    private View mKeyguardCarrierLabel;
 
     private boolean mShowCarrierInPanel = false;
 
@@ -554,8 +552,6 @@
         mNotificationPanel = (NotificationPanelView) mStatusBarWindow.findViewById(
                 R.id.notification_panel);
         mNotificationPanel.setStatusBar(this);
-        mNotificationPanelIsFullScreenWidth =
-            (mNotificationPanel.getLayoutParams().width == ViewGroup.LayoutParams.MATCH_PARENT);
 
         // make the header non-responsive to clicks
         mNotificationPanel.findViewById(R.id.header).setOnTouchListener(
@@ -609,6 +605,7 @@
         mPixelFormat = PixelFormat.OPAQUE;
 
         mSystemIconArea = (LinearLayout) mStatusBarView.findViewById(R.id.system_icon_area);
+        mSystemIcons = (LinearLayout) mStatusBarView.findViewById(R.id.system_icons);
         mStatusIcons = (LinearLayout)mStatusBarView.findViewById(R.id.statusIcons);
         mNotificationIcons = (IconMerger)mStatusBarView.findViewById(R.id.notificationIcons);
         mMoreIcon = mStatusBarView.findViewById(R.id.moreIcon);
@@ -627,39 +624,30 @@
                 (NotificationOverflowContainer) LayoutInflater.from(mContext).inflate(
                         R.layout.status_bar_notification_keyguard_overflow, mStackScroller, false);
         mKeyguardIconOverflowContainer.setOnActivatedListener(this);
-        mKeyguardCarrierLabel = mStatusBarWindow.findViewById(R.id.keyguard_carrier_text);
         mKeyguardIconOverflowContainer.setOnClickListener(mOverflowClickListener);
         mStackScroller.addView(mKeyguardIconOverflowContainer);
 
         mExpandedContents = mStackScroller;
 
-        mNotificationPanelHeader = mStatusBarWindow.findViewById(R.id.header);
+        mHeader = (StatusBarHeaderView) mStatusBarWindow.findViewById(R.id.header);
+        mHeader.setActivityStarter(this);
         mKeyguardStatusView = mStatusBarWindow.findViewById(R.id.keyguard_status_view);
         mKeyguardBottomArea =
                 (KeyguardBottomAreaView) mStatusBarWindow.findViewById(R.id.keyguard_bottom_area);
         mKeyguardBottomArea.setActivityStarter(this);
         mKeyguardIndicationTextView = (KeyguardIndicationTextView) mStatusBarWindow.findViewById(
                 R.id.keyguard_indication_text);
-        mClearButton = mStatusBarWindow.findViewById(R.id.clear_all_button);
-        mClearButton.setOnClickListener(mClearButtonListener);
-        mClearButton.setAlpha(0f);
-        mClearButton.setVisibility(View.INVISIBLE);
-        mClearButton.setEnabled(false);
         mDateView = (DateView)mStatusBarWindow.findViewById(R.id.date);
 
-        mDateTimeView = mNotificationPanelHeader.findViewById(R.id.datetime);
+        mDateTimeView = mHeader.findViewById(R.id.datetime);
         if (mDateTimeView != null) {
             mDateTimeView.setOnClickListener(mClockClickListener);
             mDateTimeView.setEnabled(true);
         }
 
-        mHeaderFlipper = (ImageView) mStatusBarWindow.findViewById(R.id.header_flipper);
-
-        if (!mNotificationPanelIsFullScreenWidth) {
-            mNotificationPanel.setSystemUiVisibility(
-                    View.STATUS_BAR_DISABLE_NOTIFICATION_ICONS |
-                    View.STATUS_BAR_DISABLE_CLOCK);
-        }
+        mNotificationPanel.setSystemUiVisibility(
+                View.STATUS_BAR_DISABLE_NOTIFICATION_ICONS |
+                View.STATUS_BAR_DISABLE_CLOCK);
 
         mTicker = new MyTicker(context, mStatusBarView);
 
@@ -680,6 +668,7 @@
                 || QuickSettings.DEBUG_GONE_TILES) {
             mRotationLockController = new RotationLockController(mContext);
         }
+        mUserInfoController = new UserInfoController(mContext);
         final SignalClusterView signalCluster =
                 (SignalClusterView)mStatusBarView.findViewById(R.id.signal_cluster);
 
@@ -734,11 +723,6 @@
         mFlipSettingsView = mSettingsContainer;
         if (mSettingsContainer != null) {
             mQS = new QuickSettings(mContext, mSettingsContainer);
-            if (!mNotificationPanelIsFullScreenWidth) {
-                mSettingsContainer.setSystemUiVisibility(
-                        View.STATUS_BAR_DISABLE_NOTIFICATION_ICONS
-                        | View.STATUS_BAR_DISABLE_SYSTEM_INFO);
-            }
             mQS.setService(this);
             mQS.setBar(mStatusBarView);
             mQS.setup(mNetworkController, mBluetoothController, mBatteryController,
@@ -747,6 +731,10 @@
             mQS = null; // fly away, be free
         }
 
+        // User info. Trigger first load.
+        mHeader.setUserInfoController(mUserInfoController);
+        mUserInfoController.reloadUserInfo();
+
         PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
         mBroadcastReceiver.onReceive(mContext,
                 new Intent(pm.isScreenOn() ? Intent.ACTION_SCREEN_ON : Intent.ACTION_SCREEN_OFF));
@@ -1118,19 +1106,6 @@
         if (mNavigationBarView != null) {
             mNavigationBarView.setLayoutDirection(layoutDirection);
         }
-
-        if (mClearButton != null && mClearButton instanceof ImageView) {
-            // Force asset reloading
-            ((ImageView)mClearButton).setImageDrawable(null);
-            ((ImageView)mClearButton).setImageResource(R.drawable.ic_notify_clear);
-        }
-
-        if (mHeaderFlipper != null) {
-            // Force asset reloading
-            mHeaderFlipper.setImageDrawable(null);
-            mHeaderFlipper.setImageResource(R.drawable.ic_notify_quicksettings);
-        }
-
         refreshAllStatusBarIcons();
     }
 
@@ -1301,38 +1276,6 @@
                     + " any=" + any + " clearable=" + clearable);
         }
 
-        if (mFlipSettingsView != null
-                && mFlipSettingsView.getVisibility() == View.VISIBLE
-                && mStackScroller.getVisibility() != View.VISIBLE) {
-            // the flip settings panel is unequivocally showing; we should not be shown
-            mClearButton.setVisibility(View.INVISIBLE);
-        } else if (mClearButton.isShown()) {
-            if (clearable != (mClearButton.getAlpha() == 1.0f)) {
-                ObjectAnimator clearAnimation = ObjectAnimator.ofFloat(
-                        mClearButton, "alpha", clearable ? 1.0f : 0.0f).setDuration(250);
-                clearAnimation.addListener(new AnimatorListenerAdapter() {
-                    @Override
-                    public void onAnimationEnd(Animator animation) {
-                        if (mClearButton.getAlpha() <= 0.0f) {
-                            mClearButton.setVisibility(View.INVISIBLE);
-                        }
-                    }
-
-                    @Override
-                    public void onAnimationStart(Animator animation) {
-                        if (mClearButton.getAlpha() <= 0.0f) {
-                            mClearButton.setVisibility(View.VISIBLE);
-                        }
-                    }
-                });
-                clearAnimation.start();
-            }
-        } else {
-            mClearButton.setAlpha(clearable ? 1.0f : 0.0f);
-            mClearButton.setVisibility(clearable ? View.VISIBLE : View.INVISIBLE);
-        }
-        mClearButton.setEnabled(clearable);
-
         final View nlo = mStatusBarView.findViewById(R.id.notification_lights_out);
         final boolean showDot = (any&&!areLightsOn());
         if (showDot != (nlo.getAlpha() == 1.0f)) {
@@ -1917,13 +1860,7 @@
 
     private void checkBarModes() {
         if (mDemoMode) return;
-        int sbMode = mStatusBarMode;
-        if (panelsEnabled() && (mInteractingWindows & StatusBarManager.WINDOW_STATUS_BAR) != 0
-                && mState != StatusBarState.KEYGUARD) {
-            // if panels are expandable, force the status bar opaque on any interaction
-            sbMode = MODE_OPAQUE;
-        }
-        checkBarMode(sbMode, mStatusBarWindowState, mStatusBarView.getBarTransitions());
+        checkBarMode(mStatusBarMode, mStatusBarWindowState, mStatusBarView.getBarTransitions());
         if (mNavigationBarView != null) {
             checkBarMode(mNavigationBarMode,
                     mNavigationBarWindowState, mNavigationBarView.getBarTransitions());
@@ -2449,10 +2386,6 @@
         final Context context = mContext;
         final Resources res = context.getResources();
 
-        if (mClearButton instanceof TextView) {
-            ((TextView)mClearButton).setText(context.getText(R.string.status_bar_clear_all_button));
-        }
-
         // Update the QuickSettings container
         if (mQS != null) mQS.updateResources();
 
@@ -2774,20 +2707,15 @@
             mKeyguardBottomArea.setVisibility(View.VISIBLE);
             mKeyguardIndicationTextView.setVisibility(View.VISIBLE);
             mKeyguardIndicationTextView.switchIndication(mKeyguardHotwordPhrase);
-            mKeyguardCarrierLabel.setVisibility(View.VISIBLE);
-            mNotificationPanelHeader.setVisibility(View.GONE);
 
             mNotificationPanel.closeQs();
-            mSettingsContainer.setKeyguardShowing(true);
         } else {
             mKeyguardStatusView.setVisibility(View.GONE);
             mKeyguardBottomArea.setVisibility(View.GONE);
             mKeyguardIndicationTextView.setVisibility(View.GONE);
-            mKeyguardCarrierLabel.setVisibility(View.GONE);
-            mNotificationPanelHeader.setVisibility(View.VISIBLE);
-
-            mSettingsContainer.setKeyguardShowing(false);
         }
+        mSettingsContainer.setKeyguardShowing(mState == StatusBarState.KEYGUARD);
+        mHeader.setKeyguardShowing(mState == StatusBarState.KEYGUARD);
 
         updateStackScrollerState();
         updatePublicMode();
@@ -2943,4 +2871,15 @@
     public ViewGroup getQuickSettingsOverlayParent() {
         return mNotificationPanel;
     }
+
+    public LinearLayout getSystemIcons() {
+        return mSystemIcons;
+    }
+
+    /**
+     * Reattaches the system icons to its normal parent in collapsed status bar.
+     */
+    public void reattachSystemIcons() {
+        mSystemIconArea.addView(mSystemIcons, 0);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
index e6de057..084bfcf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
@@ -63,7 +63,7 @@
     }
 
     @Override
-    public void onAttachedToWindow() {
+    public void onFinishInflate() {
         mBarTransitions.init();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettings.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettings.java
index f3ebf1b..8ce7279 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettings.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettings.java
@@ -323,14 +323,6 @@
         mModel.addInversionTile(inversionTile, inversionTile.getRefreshCallback());
         parent.addView(inversionTile);
 
-        // Contrast enhancement tile
-        final SystemSettingTile contrastTile = new SystemSettingTile(mContext);
-        contrastTile.setUri(Settings.Secure.ACCESSIBILITY_DISPLAY_CONTRAST_ENABLED,
-                SystemSettingTile.TYPE_SECURE);
-        contrastTile.setFragment("Settings$AccessibilityContrastSettingsActivity");
-        mModel.addContrastTile(contrastTile, contrastTile.getRefreshCallback());
-        parent.addView(contrastTile);
-
         // Color space adjustment tile
         final SystemSettingTile colorSpaceTile = new SystemSettingTile(mContext);
         colorSpaceTile.setUri(Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsModel.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsModel.java
index e1ef83a..9b90d3d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsModel.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsModel.java
@@ -95,12 +95,6 @@
     }
     static class InversionState extends State {
         boolean toggled;
-        int type;
-    }
-    static class ContrastState extends State {
-        boolean toggled;
-        float contrast;
-        float brightness;
     }
     static class ColorSpaceState extends State {
         boolean toggled;
@@ -237,38 +231,6 @@
             cr.registerContentObserver(Settings.Secure.getUriFor(
                     Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_QUICK_SETTING_ENABLED),
                     false, this, mUserTracker.getCurrentUserId());
-            cr.registerContentObserver(Settings.Secure.getUriFor(
-                    Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION),
-                    false, this, mUserTracker.getCurrentUserId());
-        }
-    }
-
-    /** ContentObserver to watch display contrast */
-    private class DisplayContrastObserver extends ContentObserver {
-        public DisplayContrastObserver(Handler handler) {
-            super(handler);
-        }
-
-        @Override
-        public void onChange(boolean selfChange) {
-            onContrastChanged();
-        }
-
-        public void startObserving() {
-            final ContentResolver cr = mContext.getContentResolver();
-            cr.unregisterContentObserver(this);
-            cr.registerContentObserver(Settings.Secure.getUriFor(
-                    Settings.Secure.ACCESSIBILITY_DISPLAY_CONTRAST_ENABLED),
-                    false, this, mUserTracker.getCurrentUserId());
-            cr.registerContentObserver(Settings.Secure.getUriFor(
-                    Settings.Secure.ACCESSIBILITY_DISPLAY_CONTRAST_QUICK_SETTING_ENABLED),
-                    false, this, mUserTracker.getCurrentUserId());
-            cr.registerContentObserver(Settings.Secure.getUriFor(
-                    Settings.Secure.ACCESSIBILITY_DISPLAY_CONTRAST),
-                    false, this, mUserTracker.getCurrentUserId());
-            cr.registerContentObserver(Settings.Secure.getUriFor(
-                    Settings.Secure.ACCESSIBILITY_DISPLAY_BRIGHTNESS),
-                    false, this, mUserTracker.getCurrentUserId());
         }
     }
 
@@ -348,7 +310,6 @@
     private final BugreportObserver mBugreportObserver;
     private final BrightnessObserver mBrightnessObserver;
     private final DisplayInversionObserver mInversionObserver;
-    private final DisplayContrastObserver mContrastObserver;
     private final DisplayColorSpaceObserver mColorSpaceObserver;
     private final ZenModeObserver mZenModeObserver;
 
@@ -417,10 +378,6 @@
     private RefreshCallback mInversionCallback;
     private InversionState mInversionState = new InversionState();
 
-    private QuickSettingsTileView mContrastTile;
-    private RefreshCallback mContrastCallback;
-    private ContrastState mContrastState = new ContrastState();
-
     private QuickSettingsTileView mColorSpaceTile;
     private RefreshCallback mColorSpaceCallback;
     private ColorSpaceState mColorSpaceState = new ColorSpaceState();
@@ -448,18 +405,17 @@
             public void onUserSwitched(int newUserId) {
                 mBrightnessObserver.startObserving();
                 mInversionObserver.startObserving();
-                mContrastObserver.startObserving();
                 mColorSpaceObserver.startObserving();
                 refreshRotationLockTile();
                 onBrightnessLevelChanged();
                 onInversionChanged();
-                onContrastChanged();
                 onColorSpaceChanged();
                 onNextAlarmChanged();
                 onBugreportChanged();
                 rebindMediaRouterAsCurrentUser();
             }
         };
+        mUserTracker.startTracking();
 
         mNextAlarmObserver = new NextAlarmObserver(mHandler);
         mNextAlarmObserver.startObserving();
@@ -469,8 +425,6 @@
         mBrightnessObserver.startObserving();
         mInversionObserver = new DisplayInversionObserver(mHandler);
         mInversionObserver.startObserving();
-        mContrastObserver = new DisplayContrastObserver(mHandler);
-        mContrastObserver.startObserving();
         mColorSpaceObserver = new DisplayColorSpaceObserver(mHandler);
         mColorSpaceObserver.startObserving();
         mZenModeObserver = new ZenModeObserver(mHandler);
@@ -1036,11 +990,8 @@
                 currentUserId) == 1;
         final boolean enabled = Settings.Secure.getIntForUser(
                 cr, Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED, 0, currentUserId) == 1;
-        final int type = Settings.Secure.getIntForUser(
-                cr, Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION, 0, currentUserId);
         mInversionState.enabled = quickSettingEnabled;
         mInversionState.toggled = enabled;
-        mInversionState.type = type;
         // TODO: Add real icon assets.
         mInversionState.iconId = enabled ? R.drawable.ic_qs_inversion_on
                 : R.drawable.ic_qs_inversion_off;
@@ -1048,36 +999,6 @@
         mInversionCallback.refreshView(mInversionTile, mInversionState);
     }
 
-    // Contrast enhancement
-    void addContrastTile(QuickSettingsTileView view, RefreshCallback cb) {
-        mContrastTile = view;
-        mContrastCallback = cb;
-        onContrastChanged();
-    }
-    public void onContrastChanged() {
-        final Resources res = mContext.getResources();
-        final ContentResolver cr = mContext.getContentResolver();
-        final int currentUserId = mUserTracker.getCurrentUserId();
-        final boolean quickSettingEnabled = Settings.Secure.getIntForUser(
-                cr, Settings.Secure.ACCESSIBILITY_DISPLAY_CONTRAST_QUICK_SETTING_ENABLED, 0,
-                currentUserId) == 1;
-        final boolean enabled = Settings.Secure.getIntForUser(
-                cr, Settings.Secure.ACCESSIBILITY_DISPLAY_CONTRAST_ENABLED, 0, currentUserId) == 1;
-        final float contrast = Settings.Secure.getFloatForUser(
-                cr, Settings.Secure.ACCESSIBILITY_DISPLAY_CONTRAST, 1, currentUserId);
-        final float brightness = Settings.Secure.getFloatForUser(
-                cr, Settings.Secure.ACCESSIBILITY_DISPLAY_BRIGHTNESS, 0, currentUserId);
-        mContrastState.enabled = quickSettingEnabled;
-        mContrastState.toggled = enabled;
-        mContrastState.contrast = contrast;
-        mContrastState.brightness = brightness;
-        // TODO: Add real icon assets.
-        mContrastState.iconId = enabled ? R.drawable.ic_qs_contrast_on
-                : R.drawable.ic_qs_contrast_off;
-        mContrastState.label = res.getString(R.string.quick_settings_contrast_label);
-        mContrastCallback.refreshView(mContrastTile, mContrastState);
-    }
-
     // Color space adjustment
     void addColorSpaceTile(QuickSettingsTileView view, RefreshCallback cb) {
         mColorSpaceTile = view;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
index 9d33930..5527473 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
@@ -17,25 +17,52 @@
 package com.android.systemui.statusbar.phone;
 
 import android.content.Context;
+import android.content.Intent;
+import android.graphics.Outline;
+import android.graphics.Rect;
 import android.util.AttributeSet;
 import android.view.View;
 import android.view.ViewGroup;
+import android.widget.ImageView;
 import android.widget.LinearLayout;
 import android.widget.RelativeLayout;
 
 import com.android.systemui.R;
+import com.android.systemui.settings.BrightnessController;
+import com.android.systemui.settings.ToggleSlider;
+import com.android.systemui.statusbar.policy.UserInfoController;
 
 /**
  * The view to manage the header area in the expanded status bar.
  */
-public class StatusBarHeaderView extends RelativeLayout {
+public class StatusBarHeaderView extends RelativeLayout implements View.OnClickListener {
 
     private boolean mExpanded;
+    private boolean mKeyguardShowing;
+
     private View mBackground;
-    private View mFlipper;
+    private ViewGroup mSystemIconsContainer;
+    private View mDateTime;
+    private View mKeyguardCarrierText;
+    private MultiUserSwitch mMultiUserSwitch;
+    private View mDate;
+    private View mStatusIcons;
+    private View mSignalCluster;
+    private View mSettingsButton;
+    private View mBrightnessContainer;
 
     private int mCollapsedHeight;
     private int mExpandedHeight;
+    private int mKeyguardHeight;
+
+    private int mKeyguardWidth = ViewGroup.LayoutParams.MATCH_PARENT;
+    private int mNormalWidth;
+
+    private ActivityStarter mActivityStarter;
+    private BrightnessController mBrightnessController;
+
+    private final Rect mClipBounds = new Rect();
+    private final Outline mOutline = new Outline();
 
     public StatusBarHeaderView(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -45,19 +72,35 @@
     protected void onFinishInflate() {
         super.onFinishInflate();
         mBackground = findViewById(R.id.background);
-        mFlipper = findViewById(R.id.header_flipper);
+        mSystemIconsContainer = (ViewGroup) findViewById(R.id.system_icons_container);
+        mDateTime = findViewById(R.id.datetime);
+        mKeyguardCarrierText = findViewById(R.id.keyguard_carrier_text);
+        mMultiUserSwitch = (MultiUserSwitch) findViewById(R.id.multi_user_switch);
+        mDate = findViewById(R.id.date);
+        mSettingsButton = findViewById(R.id.settings_button);
+        mSettingsButton.setOnClickListener(this);
+        mBrightnessContainer = findViewById(R.id.brightness_container);
+        mBrightnessController = new BrightnessController(getContext(),
+                (ImageView) findViewById(R.id.brightness_icon),
+                (ToggleSlider) findViewById(R.id.brightness_slider));
         loadDimens();
     }
 
     private void loadDimens() {
-        mCollapsedHeight = getResources().getDimensionPixelSize(
-                R.dimen.status_bar_header_height);
+        mCollapsedHeight = getResources().getDimensionPixelSize(R.dimen.status_bar_header_height);
         mExpandedHeight = getResources().getDimensionPixelSize(
                 R.dimen.status_bar_header_height_expanded);
+        mKeyguardHeight = getResources().getDimensionPixelSize(
+                R.dimen.status_bar_header_height_keyguard);
+        mNormalWidth = getLayoutParams().width;
+    }
+
+    public void setActivityStarter(ActivityStarter activityStarter) {
+        mActivityStarter = activityStarter;
     }
 
     public int getCollapsedHeight() {
-        return mCollapsedHeight;
+        return mKeyguardShowing ? mKeyguardHeight : mCollapsedHeight;
     }
 
     public int getExpandedHeight() {
@@ -65,16 +108,82 @@
     }
 
     public void setExpanded(boolean expanded) {
-        if (expanded != mExpanded) {
-            ViewGroup.LayoutParams lp = getLayoutParams();
-            lp.height = expanded ? mExpandedHeight : mCollapsedHeight;
-            setLayoutParams(lp);
-            mExpanded = expanded;
+        boolean changed = expanded != mExpanded;
+        mExpanded = expanded;
+        if (changed) {
+            updateHeights();
+            updateVisibilities();
+            updateSystemIconsLayoutParams();
+            updateBrightnessControllerState();
         }
     }
 
-    public void setExpansionEnabled(boolean enabled) {
-        mFlipper.setVisibility(enabled ? View.VISIBLE : View.GONE);
+    private void updateHeights() {
+        boolean onKeyguardAndCollapsed = mKeyguardShowing && !mExpanded;
+        int height;
+        if (mExpanded) {
+            height = mExpandedHeight;
+        } else if (onKeyguardAndCollapsed) {
+            height = mKeyguardHeight;
+        } else {
+            height = mCollapsedHeight;
+        }
+        ViewGroup.LayoutParams lp = getLayoutParams();
+        if (lp.height != height) {
+            lp.height = height;
+            setLayoutParams(lp);
+        }
+        int systemIconsContainerHeight = onKeyguardAndCollapsed ? mKeyguardHeight : mCollapsedHeight;
+        lp = mSystemIconsContainer.getLayoutParams();
+        if (lp.height != systemIconsContainerHeight) {
+            lp.height = systemIconsContainerHeight;
+            mSystemIconsContainer.setLayoutParams(lp);
+        }
+        lp = mMultiUserSwitch.getLayoutParams();
+        if (lp.height != systemIconsContainerHeight) {
+            lp.height = systemIconsContainerHeight;
+            mMultiUserSwitch.setLayoutParams(lp);
+        }
+    }
+
+    private void updateWidth() {
+        int width = mKeyguardShowing ? mKeyguardWidth : mNormalWidth;
+        ViewGroup.LayoutParams lp = getLayoutParams();
+        if (width != lp.width) {
+            lp.width = width;
+            setLayoutParams(lp);
+        }
+    }
+
+    private void updateVisibilities() {
+        boolean onKeyguardAndCollapsed = mKeyguardShowing && !mExpanded;
+        mBackground.setVisibility(onKeyguardAndCollapsed ? View.INVISIBLE : View.VISIBLE);
+        mDateTime.setVisibility(onKeyguardAndCollapsed ? View.INVISIBLE : View.VISIBLE);
+        mKeyguardCarrierText.setVisibility(onKeyguardAndCollapsed ? View.VISIBLE : View.GONE);
+        mDate.setVisibility(mExpanded ? View.VISIBLE : View.GONE);
+        mSettingsButton.setVisibility(mExpanded ? View.VISIBLE : View.GONE);
+        mBrightnessContainer.setVisibility(mExpanded ? View.VISIBLE : View.GONE);
+        if (mStatusIcons != null) {
+            mStatusIcons.setVisibility(!mExpanded ? View.VISIBLE : View.GONE);
+        }
+        if (mSignalCluster != null) {
+            mSignalCluster.setVisibility(!mExpanded ? View.VISIBLE : View.GONE);
+        }
+    }
+
+    private void updateSystemIconsLayoutParams() {
+        RelativeLayout.LayoutParams lp = (LayoutParams) mSystemIconsContainer.getLayoutParams();
+        lp.addRule(RelativeLayout.START_OF, mExpanded
+                ? mSettingsButton.getId()
+                : mMultiUserSwitch.getId());
+    }
+
+    private void updateBrightnessControllerState() {
+        if (mExpanded) {
+            mBrightnessController.registerCallbacks();
+        } else {
+            mBrightnessController.unregisterCallbacks();
+        }
     }
 
     public void setExpansion(float height) {
@@ -89,9 +198,65 @@
         } else {
             mBackground.setTranslationY(0);
         }
+        setClipping(height);
+    }
+
+    private void setClipping(float height) {
+        mClipBounds.set(getPaddingLeft(), 0, getWidth() - getPaddingRight(), (int) height);
+        setClipBounds(mClipBounds);
+        mOutline.setRect(mClipBounds);
+        setOutline(mOutline);
     }
 
     public View getBackgroundView() {
         return mBackground;
     }
+
+    public void attachSystemIcons(LinearLayout systemIcons) {
+        mSystemIconsContainer.addView(systemIcons);
+        mStatusIcons = systemIcons.findViewById(R.id.statusIcons);
+        mSignalCluster = systemIcons.findViewById(R.id.signal_cluster);
+    }
+
+    public void onSystemIconsDetached() {
+        if (mStatusIcons != null) {
+            mStatusIcons.setVisibility(View.VISIBLE);
+        }
+        if (mSignalCluster != null) {
+            mSignalCluster.setVisibility(View.VISIBLE);
+        }
+        mStatusIcons = null;
+        mSignalCluster = null;
+    }
+
+    public void setKeyguardShowing(boolean keyguardShowing) {
+        mKeyguardShowing = keyguardShowing;
+        if (keyguardShowing) {
+            setZ(0);
+        } else {
+            setTranslationZ(0);
+        }
+        updateHeights();
+        updateWidth();
+        updateVisibilities();
+    }
+
+    public void setUserInfoController(UserInfoController userInfoController) {
+        mMultiUserSwitch.setUserInfoController(userInfoController);
+    }
+
+    public void setOverlayParent(ViewGroup parent) {
+        mMultiUserSwitch.setOverlayParent(parent);
+    }
+
+    @Override
+    public void onClick(View v) {
+        if (v == mSettingsButton) {
+            startSettingsActivity();
+        }
+    }
+
+    private void startSettingsActivity() {
+        mActivityStarter.startActivity(new Intent(android.provider.Settings.ACTION_SETTINGS));
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index f24c1b6..48c54fc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -73,8 +73,7 @@
     public void show(Bundle options) {
         mShowing = true;
         mStatusBarWindowManager.setKeyguardShowing(true);
-        showBouncerOrKeyguard();
-        updateStates();
+        reset();
     }
 
     /**
@@ -105,13 +104,15 @@
      * Reset the state of the view.
      */
     public void reset() {
-        if (mOccluded) {
-            mPhoneStatusBar.hideKeyguard();
-            mBouncer.hide();
-        } else {
-            showBouncerOrKeyguard();
+        if (mShowing) {
+            if (mOccluded) {
+                mPhoneStatusBar.hideKeyguard();
+                mBouncer.hide();
+            } else {
+                showBouncerOrKeyguard();
+            }
+            updateStates();
         }
-        updateStates();
     }
 
     public void onScreenTurnedOff() {
@@ -121,7 +122,6 @@
 
     public void onScreenTurnedOn(final IKeyguardShowCallback callback) {
         mScreenOn = true;
-        reset();
         if (callback != null) {
             callbackAfterDraw(callback);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
index a4c9df5..8809d18 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
@@ -78,9 +78,8 @@
                         | WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION
                         | WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS,
                 PixelFormat.TRANSLUCENT);
-
         mLp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
-        mLp.gravity = Gravity.TOP | Gravity.FILL_HORIZONTAL;
+        mLp.gravity = Gravity.TOP;
         mLp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
         mLp.setTitle("StatusBar");
         mLp.packageName = mContext.getPackageName();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
index 8ced1c9..55a0bba 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
@@ -20,6 +20,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.res.TypedArray;
 import android.os.Bundle;
 import android.text.Spannable;
 import android.text.SpannableStringBuilder;
@@ -30,6 +31,7 @@
 import android.widget.TextView;
 
 import com.android.systemui.DemoMode;
+import com.android.systemui.R;
 
 import java.text.SimpleDateFormat;
 import java.util.Calendar;
@@ -52,7 +54,7 @@
     private static final int AM_PM_STYLE_SMALL   = 1;
     private static final int AM_PM_STYLE_GONE    = 2;
 
-    private static final int AM_PM_STYLE = AM_PM_STYLE_GONE;
+    private final int mAmPmStyle;
 
     public Clock(Context context) {
         this(context, null);
@@ -64,6 +66,15 @@
 
     public Clock(Context context, AttributeSet attrs, int defStyle) {
         super(context, attrs, defStyle);
+        TypedArray a = context.getTheme().obtainStyledAttributes(
+                attrs,
+                R.styleable.Clock,
+                0, 0);
+        try {
+            mAmPmStyle = a.getInt(R.styleable.Clock_amPmStyle, AM_PM_STYLE_GONE);
+        } finally {
+            a.recycle();
+        }
     }
 
     @Override
@@ -145,7 +156,7 @@
              * add dummy characters around it to let us find it again after
              * formatting and change its size.
              */
-            if (AM_PM_STYLE != AM_PM_STYLE_NORMAL) {
+            if (mAmPmStyle != AM_PM_STYLE_NORMAL) {
                 int a = -1;
                 boolean quoted = false;
                 for (int i = 0; i < format.length(); i++) {
@@ -177,15 +188,15 @@
         }
         String result = sdf.format(mCalendar.getTime());
 
-        if (AM_PM_STYLE != AM_PM_STYLE_NORMAL) {
+        if (mAmPmStyle != AM_PM_STYLE_NORMAL) {
             int magic1 = result.indexOf(MAGIC1);
             int magic2 = result.indexOf(MAGIC2);
             if (magic1 >= 0 && magic2 > magic1) {
                 SpannableStringBuilder formatted = new SpannableStringBuilder(result);
-                if (AM_PM_STYLE == AM_PM_STYLE_GONE) {
+                if (mAmPmStyle == AM_PM_STYLE_GONE) {
                     formatted.delete(magic1, magic2+1);
                 } else {
-                    if (AM_PM_STYLE == AM_PM_STYLE_SMALL) {
+                    if (mAmPmStyle == AM_PM_STYLE_SMALL) {
                         CharacterStyle style = new RelativeSizeSpan(0.7f);
                         formatted.setSpan(style, magic1, magic2,
                                           Spannable.SPAN_EXCLUSIVE_INCLUSIVE);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserInfoController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserInfoController.java
new file mode 100644
index 0000000..173af40
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserInfoController.java
@@ -0,0 +1,215 @@
+/*
+ * 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.policy;
+
+import android.app.ActivityManagerNative;
+import android.bluetooth.BluetoothAdapter;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.content.pm.UserInfo;
+import android.database.Cursor;
+import android.graphics.Bitmap;
+import android.graphics.BitmapShader;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffXfermode;
+import android.graphics.Rect;
+import android.graphics.Shader;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.hardware.display.DisplayManager;
+import android.os.AsyncTask;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.provider.ContactsContract;
+import android.security.KeyChain;
+import android.util.Log;
+import android.util.Pair;
+
+import com.android.internal.view.RotationPolicy;
+import com.android.systemui.R;
+
+import java.util.ArrayList;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+public final class UserInfoController {
+
+    private static final String TAG = "UserInfoController";
+
+    private final Context mContext;
+    private final ArrayList<OnUserInfoChangedListener> mCallbacks =
+            new ArrayList<OnUserInfoChangedListener>();
+    private AsyncTask<Void, Void, Pair<String, Drawable>> mUserInfoTask;
+
+    private boolean mUseDefaultAvatar;
+    private String mUserName;
+    private Drawable mUserDrawable;
+
+    public UserInfoController(Context context) {
+        mContext = context;
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(Intent.ACTION_USER_SWITCHED);
+        filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
+        mContext.registerReceiver(mReceiver, filter);
+
+        IntentFilter profileFilter = new IntentFilter();
+        profileFilter.addAction(ContactsContract.Intents.ACTION_PROFILE_CHANGED);
+        profileFilter.addAction(Intent.ACTION_USER_INFO_CHANGED);
+        mContext.registerReceiverAsUser(mProfileReceiver, UserHandle.ALL, profileFilter,
+                null, null);
+    }
+
+    public void addListener(OnUserInfoChangedListener callback) {
+        mCallbacks.add(callback);
+    }
+
+    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            final String action = intent.getAction();
+            if (Intent.ACTION_USER_SWITCHED.equals(action)) {
+                reloadUserInfo();
+            } else if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) {
+                if (mUseDefaultAvatar) {
+                    reloadUserInfo();
+                }
+            }
+        }
+    };
+
+    private final BroadcastReceiver mProfileReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            final String action = intent.getAction();
+            if (ContactsContract.Intents.ACTION_PROFILE_CHANGED.equals(action) ||
+                    Intent.ACTION_USER_INFO_CHANGED.equals(action)) {
+                try {
+                    final int currentUser = ActivityManagerNative.getDefault().getCurrentUser().id;
+                    final int changedUser =
+                            intent.getIntExtra(Intent.EXTRA_USER_HANDLE, getSendingUserId());
+                    if (changedUser == currentUser) {
+                        reloadUserInfo();
+                    }
+                } catch (RemoteException e) {
+                    Log.e(TAG, "Couldn't get current user id for profile change", e);
+                }
+            }
+        }
+    };
+
+    public void reloadUserInfo() {
+        if (mUserInfoTask != null) {
+            mUserInfoTask.cancel(false);
+            mUserInfoTask = null;
+        }
+        queryForUserInformation();
+    }
+
+    private Bitmap circularClip(Bitmap input) {
+        Bitmap output = Bitmap.createBitmap(input.getWidth(),
+                input.getHeight(), Bitmap.Config.ARGB_8888);
+        Canvas canvas = new Canvas(output);
+        final Paint paint = new Paint();
+        paint.setShader(new BitmapShader(input, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP));
+        paint.setAntiAlias(true);
+        canvas.drawCircle(input.getWidth() / 2, input.getHeight() / 2, input.getWidth() / 2, paint);
+        return output;
+    }
+
+    private void queryForUserInformation() {
+        Context currentUserContext;
+        UserInfo userInfo;
+        try {
+            userInfo = ActivityManagerNative.getDefault().getCurrentUser();
+            currentUserContext = mContext.createPackageContextAsUser("android", 0,
+                    new UserHandle(userInfo.id));
+        } catch (PackageManager.NameNotFoundException e) {
+            Log.e(TAG, "Couldn't create user context", e);
+            throw new RuntimeException(e);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Couldn't get user info", e);
+            throw new RuntimeException(e);
+        }
+        final int userId = userInfo.id;
+        final String userName = userInfo.name;
+
+        final Context context = currentUserContext;
+        mUserInfoTask = new AsyncTask<Void, Void, Pair<String, Drawable>>() {
+            @Override
+            protected Pair<String, Drawable> doInBackground(Void... params) {
+                final UserManager um = UserManager.get(mContext);
+
+                // Fall back to the UserManager nickname if we can't read the name from the local
+                // profile below.
+                String name = userName;
+                Drawable avatar = null;
+                Bitmap rawAvatar = um.getUserIcon(userId);
+                if (rawAvatar != null) {
+                    avatar = new BitmapDrawable(mContext.getResources(), circularClip(rawAvatar));
+                } else {
+                    avatar = mContext.getResources().getDrawable(R.drawable.ic_qs_default_user);
+                    mUseDefaultAvatar = true;
+                }
+
+                // If it's a single-user device, get the profile name, since the nickname is not
+                // usually valid
+                if (um.getUsers().size() <= 1) {
+                    // Try and read the display name from the local profile
+                    final Cursor cursor = context.getContentResolver().query(
+                            ContactsContract.Profile.CONTENT_URI, new String[] {
+                            ContactsContract.CommonDataKinds.Phone._ID, ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME},
+                            null, null, null);
+                    if (cursor != null) {
+                        try {
+                            if (cursor.moveToFirst()) {
+                                name = cursor.getString(cursor.getColumnIndex(
+                                        ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
+                            }
+                        } finally {
+                            cursor.close();
+                        }
+                    }
+                }
+                return new Pair<String, Drawable>(name, avatar);
+            }
+
+            @Override
+            protected void onPostExecute(Pair<String, Drawable> result) {
+                mUserName = result.first;
+                mUserDrawable = result.second;
+                mUserInfoTask = null;
+                notifyChanged();
+            }
+        };
+        mUserInfoTask.execute();
+    }
+
+    private void notifyChanged() {
+        for (OnUserInfoChangedListener listener : mCallbacks) {
+            listener.onUserInfoChanged(mUserName, mUserDrawable);
+        }
+    }
+
+    public interface OnUserInfoChangedListener {
+        public void onUserInfoChanged(String name, Drawable picture);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java
index 4121a40..deab757 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java
@@ -28,6 +28,8 @@
     private int mScrollY;
     private boolean mDimmed;
     private View mActivatedChild;
+    private float mOverScrollTopAmount;
+    private float mOverScrollBottomAmount;
 
     public int getScrollY() {
         return mScrollY;
@@ -72,4 +74,16 @@
     public View getActivatedChild() {
         return mActivatedChild;
     }
+
+    public void setOverScrollAmount(float amount, boolean onTop) {
+        if (onTop) {
+            mOverScrollTopAmount = amount;
+        } else {
+            mOverScrollBottomAmount = amount;
+        }
+    }
+
+    public float getOverScrollAmount(boolean top) {
+        return top ? mOverScrollTopAmount : mOverScrollBottomAmount;
+    }
 }
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 afd5068..fbb6695 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -53,6 +53,7 @@
 
     private static final String TAG = "NotificationStackScrollLayout";
     private static final boolean DEBUG = false;
+    private static final float RUBBER_BAND_FACTOR = 0.35f;
 
     /**
      * Sentinel value for no current active pointer. Used by {@link #mActivePointerId}.
@@ -70,8 +71,8 @@
     private int mTouchSlop;
     private int mMinimumVelocity;
     private int mMaximumVelocity;
-    private int mOverscrollDistance;
     private int mOverflingDistance;
+    private float mMaxOverScroll;
     private boolean mIsBeingDragged;
     private int mLastMotionY;
     private int mActivePointerId;
@@ -80,9 +81,12 @@
     private Paint mDebugPaint;
     private int mContentHeight;
     private int mCollapsedSize;
+    private int mBottomStackSlowDownHeight;
     private int mBottomStackPeekSize;
     private int mEmptyMarginBottom;
     private int mPaddingBetweenElements;
+    private int mPaddingBetweenElementsDimmed;
+    private int mPaddingBetweenElementsNormal;
     private int mTopPadding;
 
     /**
@@ -104,6 +108,16 @@
     private ArrayList<View> mSwipedOutViews = new ArrayList<View>();
     private final StackStateAnimator mStateAnimator = new StackStateAnimator(this);
 
+    /**
+     * The raw amount of the overScroll on the top, which is not rubber-banded.
+     */
+    private float mOverScrolledTopPixels;
+
+    /**
+     * The raw amount of the overScroll on the bottom, which is not rubber-banded.
+     */
+    private float mOverScrolledBottomPixels;
+
     private OnChildLocationsChangedListener mListener;
     private ExpandableView.OnHeightChangedListener mOnHeightChangedListener;
     private boolean mNeedsAnimation;
@@ -153,7 +167,10 @@
         if (DEBUG) {
             int y = mCollapsedSize;
             canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
-            y = (int) (getLayoutHeight() - mBottomStackPeekSize - mCollapsedSize);
+            y = (int) (getLayoutHeight() - mBottomStackPeekSize
+                    - mBottomStackSlowDownHeight);
+            canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
+            y = (int) (getLayoutHeight() - mBottomStackPeekSize);
             canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
             y = (int) getLayoutHeight();
             canvas.drawLine(0, y, getWidth(), y, mDebugPaint);
@@ -169,7 +186,6 @@
         mTouchSlop = configuration.getScaledTouchSlop();
         mMinimumVelocity = configuration.getScaledMinimumFlingVelocity();
         mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
-        mOverscrollDistance = configuration.getScaledOverscrollDistance();
         mOverflingDistance = configuration.getScaledOverflingDistance();
         float densityScale = getResources().getDisplayMetrics().density;
         float pagingTouchSlop = ViewConfiguration.get(getContext()).getScaledPagingTouchSlop();
@@ -183,9 +199,20 @@
                 .getDimensionPixelSize(R.dimen.bottom_stack_peek_amount);
         mEmptyMarginBottom = context.getResources().getDimensionPixelSize(
                 R.dimen.notification_stack_margin_bottom);
-        mPaddingBetweenElements = context.getResources()
-                .getDimensionPixelSize(R.dimen.notification_padding);
         mStackScrollAlgorithm = new StackScrollAlgorithm(context);
+        mPaddingBetweenElementsDimmed = context.getResources()
+                .getDimensionPixelSize(R.dimen.notification_padding_dimmed);
+        mPaddingBetweenElementsNormal = context.getResources()
+                .getDimensionPixelSize(R.dimen.notification_padding);
+        updatePadding(false);
+    }
+
+    private void updatePadding(boolean dimmed) {
+        mPaddingBetweenElements = dimmed
+                ? mPaddingBetweenElementsDimmed
+                : mPaddingBetweenElementsNormal;
+        mBottomStackSlowDownHeight = mStackScrollAlgorithm.getBottomStackSlowDownLength();
+        updateContentHeight();
     }
 
     @Override
@@ -527,7 +554,7 @@
                  * will be false if being flinged.
                  */
                 if (!mScroller.isFinished()) {
-                    mScroller.abortAnimation();
+                    mScroller.forceFinished(true);
                 }
 
                 // Remember where the motion event started
@@ -555,40 +582,23 @@
                 if (mIsBeingDragged) {
                     // Scroll to follow the motion event
                     mLastMotionY = y;
-
-                    final int oldX = mScrollX;
-                    final int oldY = mOwnScrollY;
                     final int range = getScrollRange();
-                    final int overscrollMode = getOverScrollMode();
-                    final boolean canOverscroll = overscrollMode == OVER_SCROLL_ALWAYS ||
-                            (overscrollMode == OVER_SCROLL_IF_CONTENT_SCROLLS && range > 0);
+
+                    float scrollAmount;
+                    if (deltaY < 0) {
+                        scrollAmount = overScrollDown(deltaY);
+                    } else {
+                        scrollAmount = overScrollUp(deltaY, range);
+                    }
 
                     // Calling overScrollBy will call onOverScrolled, which
                     // calls onScrollChanged if applicable.
-                    if (overScrollBy(0, deltaY, 0, mOwnScrollY,
-                            0, range, 0, mOverscrollDistance, true)) {
-                        // Break our velocity if we hit a scroll barrier.
-                        mVelocityTracker.clear();
+                    if (scrollAmount != 0.0f) {
+                        // The scrolling motion could not be compensated with the
+                        // existing overScroll, we have to scroll the view
+                        overScrollBy(0, (int) scrollAmount, 0, mOwnScrollY,
+                                0, range, 0, getHeight() / 2, true);
                     }
-                    // TODO: Overscroll
-//                    if (canOverscroll) {
-//                        final int pulledToY = oldY + deltaY;
-//                        if (pulledToY < 0) {
-//                            mEdgeGlowTop.onPull((float) deltaY / getHeight());
-//                            if (!mEdgeGlowBottom.isFinished()) {
-//                                mEdgeGlowBottom.onRelease();
-//                            }
-//                        } else if (pulledToY > range) {
-//                            mEdgeGlowBottom.onPull((float) deltaY / getHeight());
-//                            if (!mEdgeGlowTop.isFinished()) {
-//                                mEdgeGlowTop.onRelease();
-//                            }
-//                        }
-//                        if (mEdgeGlowTop != null
-//                                && (!mEdgeGlowTop.isFinished() || !mEdgeGlowBottom.isFinished())){
-//                            postInvalidateOnAnimation();
-//                        }
-//                    }
                 }
                 break;
             case MotionEvent.ACTION_UP:
@@ -635,6 +645,68 @@
         return true;
     }
 
+    /**
+     * Perform a scroll upwards and adapt the overscroll amounts accordingly
+     *
+     * @param deltaY The amount to scroll upwards, has to be positive.
+     * @return The amount of scrolling to be performed by the scroller,
+     *         not handled by the overScroll amount.
+     */
+    private float overScrollUp(int deltaY, int range) {
+        deltaY = Math.max(deltaY, 0);
+        float currentTopAmount = getCurrentOverScrollAmount(true);
+        float newTopAmount = currentTopAmount - deltaY;
+        if (currentTopAmount > 0) {
+            setOverScrollAmount(newTopAmount, true /* onTop */,
+                    false /* animate */);
+        }
+        // Top overScroll might not grab all scrolling motion,
+        // we have to scroll as well.
+        float scrollAmount = newTopAmount < 0 ? -newTopAmount : 0.0f;
+        float newScrollY = mOwnScrollY + scrollAmount;
+        if (newScrollY > range) {
+            float currentBottomPixels = getCurrentOverScrolledPixels(false);
+            // We overScroll on the top
+            setOverScrolledPixels(currentBottomPixels + newScrollY - range,
+                    false /* onTop */,
+                    false /* animate */);
+            mOwnScrollY = range;
+            scrollAmount = 0.0f;
+        }
+        return scrollAmount;
+    }
+
+    /**
+     * Perform a scroll downward and adapt the overscroll amounts accordingly
+     *
+     * @param deltaY The amount to scroll downwards, has to be negative.
+     * @return The amount of scrolling to be performed by the scroller,
+     *         not handled by the overScroll amount.
+     */
+    private float overScrollDown(int deltaY) {
+        deltaY = Math.min(deltaY, 0);
+        float currentBottomAmount = getCurrentOverScrollAmount(false);
+        float newBottomAmount = currentBottomAmount + deltaY;
+        if (currentBottomAmount > 0) {
+            setOverScrollAmount(newBottomAmount, false /* onTop */,
+                    false /* animate */);
+        }
+        // Bottom overScroll might not grab all scrolling motion,
+        // we have to scroll as well.
+        float scrollAmount = newBottomAmount < 0 ? newBottomAmount : 0.0f;
+        float newScrollY = mOwnScrollY + scrollAmount;
+        if (newScrollY < 0) {
+            float currentTopPixels = getCurrentOverScrolledPixels(true);
+            // We overScroll on the top
+            setOverScrolledPixels(currentTopPixels - newScrollY,
+                    true /* onTop */,
+                    false /* animate */);
+            mOwnScrollY = 0;
+            scrollAmount = 0.0f;
+        }
+        return scrollAmount;
+    }
+
     private void onSecondaryPointerUp(MotionEvent ev) {
         final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >>
                 MotionEvent.ACTION_POINTER_INDEX_SHIFT;
@@ -684,23 +756,16 @@
 
             if (oldX != x || oldY != y) {
                 final int range = getScrollRange();
-                final int overscrollMode = getOverScrollMode();
-                final boolean canOverscroll = overscrollMode == OVER_SCROLL_ALWAYS ||
-                        (overscrollMode == OVER_SCROLL_IF_CONTENT_SCROLLS && range > 0);
+                if (y < 0 && oldY >= 0 || y > range && oldY <= range) {
+                    float currVelocity = mScroller.getCurrVelocity();
+                    if (currVelocity >= mMinimumVelocity) {
+                        mMaxOverScroll = Math.abs(currVelocity) / 1000 * mOverflingDistance;
+                    }
+                }
 
                 overScrollBy(x - oldX, y - oldY, oldX, oldY, 0, range,
-                        0, mOverflingDistance, false);
+                        0, (int) (mMaxOverScroll), false);
                 onScrollChanged(mScrollX, mOwnScrollY, oldX, oldY);
-
-                if (canOverscroll) {
-                    // TODO: Overscroll
-//                    if (y < 0 && oldY >= 0) {
-//                        mEdgeGlowTop.onAbsorb((int) mScroller.getCurrVelocity());
-//                    } else if (y > range && oldY <= range) {
-//                        mEdgeGlowBottom.onAbsorb((int) mScroller.getCurrVelocity());
-//                    }
-                }
-                updateChildren();
             }
 
             // Keep on drawing until the animation has finished.
@@ -708,6 +773,81 @@
         }
     }
 
+    @Override
+    protected int computeVerticalScrollRange() {
+        // needed for the overScroller
+        return mContentHeight;
+    }
+
+    /**
+     * Set the amount of overScrolled pixels which will force the view to apply a rubber-banded
+     * overscroll effect based on numPixels. By default this will also cancel animations on the
+     * same overScroll edge.
+     *
+     * @param numPixels The amount of pixels to overScroll by. These will be scaled according to
+     *                  the rubber-banding logic.
+     * @param onTop Should the effect be applied on top of the scroller.
+     * @param animate Should an animation be performed.
+     */
+    public void setOverScrolledPixels(float numPixels, boolean onTop, boolean animate) {
+        setOverScrollAmount(numPixels * RUBBER_BAND_FACTOR, onTop, animate, true);
+    }
+
+    /**
+     * Set the effective overScroll amount which will be directly reflected in the layout.
+     * By default this will also cancel animations on the same overScroll edge.
+     *
+     * @param amount The amount to overScroll by.
+     * @param onTop Should the effect be applied on top of the scroller.
+     * @param animate Should an animation be performed.
+     */
+    public void setOverScrollAmount(float amount, boolean onTop, boolean animate) {
+        setOverScrollAmount(amount, onTop, animate, true);
+    }
+
+    /**
+     * Set the effective overScroll amount which will be directly reflected in the layout.
+     *
+     * @param amount The amount to overScroll by.
+     * @param onTop Should the effect be applied on top of the scroller.
+     * @param animate Should an animation be performed.
+     * @param cancelAnimators Should running animations be cancelled.
+     */
+    public void setOverScrollAmount(float amount, boolean onTop, boolean animate,
+            boolean cancelAnimators) {
+        if (cancelAnimators) {
+            mStateAnimator.cancelOverScrollAnimators(onTop);
+        }
+        setOverScrollAmountInternal(amount, onTop, animate);
+    }
+
+    private void setOverScrollAmountInternal(float amount, boolean onTop, boolean animate) {
+        amount = Math.max(0, amount);
+        if (animate) {
+            mStateAnimator.animateOverScrollToAmount(amount, onTop);
+        } else {
+            setOverScrolledPixels(amount / RUBBER_BAND_FACTOR, onTop);
+            mAmbientState.setOverScrollAmount(amount, onTop);
+            requestChildrenUpdate();
+        }
+    }
+
+    public float getCurrentOverScrollAmount(boolean top) {
+        return mAmbientState.getOverScrollAmount(top);
+    }
+
+    public float getCurrentOverScrolledPixels(boolean top) {
+        return top? mOverScrolledTopPixels : mOverScrolledBottomPixels;
+    }
+
+    private void setOverScrolledPixels(float amount, boolean onTop) {
+        if (onTop) {
+            mOverScrolledTopPixels = amount;
+        } else {
+            mOverScrolledBottomPixels = amount;
+        }
+    }
+
     private void customScrollTo(int y) {
         mOwnScrollY = y;
         updateChildren();
@@ -721,33 +861,51 @@
             final int oldY = mOwnScrollY;
             mScrollX = scrollX;
             mOwnScrollY = scrollY;
-            invalidateParentIfNeeded();
-            onScrollChanged(mScrollX, mOwnScrollY, oldX, oldY);
             if (clampedY) {
-                mScroller.springBack(mScrollX, mOwnScrollY, 0, 0, 0, getScrollRange());
+                springBack();
+            } else {
+                onScrollChanged(mScrollX, mOwnScrollY, oldX, oldY);
+                invalidateParentIfNeeded();
+                updateChildren();
             }
-            updateChildren();
         } else {
             customScrollTo(scrollY);
             scrollTo(scrollX, mScrollY);
         }
     }
 
+    private void springBack() {
+        int scrollRange = getScrollRange();
+        boolean overScrolledTop = mOwnScrollY <= 0;
+        boolean overScrolledBottom = mOwnScrollY >= scrollRange;
+        if (overScrolledTop || overScrolledBottom) {
+            boolean onTop;
+            float newAmount;
+            if (overScrolledTop) {
+                onTop = true;
+                newAmount = -mOwnScrollY;
+                mOwnScrollY = 0;
+            } else {
+                onTop = false;
+                newAmount = mOwnScrollY - scrollRange;
+                mOwnScrollY = scrollRange;
+            }
+            setOverScrollAmount(newAmount, onTop, false);
+            setOverScrollAmount(0.0f, onTop, true);
+            mScroller.forceFinished(true);
+        }
+    }
+
     private int getScrollRange() {
         int scrollRange = 0;
         ExpandableView firstChild = (ExpandableView) getFirstChildNotGone();
         if (firstChild != null) {
             int contentHeight = getContentHeight();
             int firstChildMaxExpandHeight = getMaxExpandHeight(firstChild);
-
-            scrollRange = Math.max(0, contentHeight - mMaxLayoutHeight + mBottomStackPeekSize);
+            scrollRange = Math.max(0, contentHeight - mMaxLayoutHeight + mBottomStackPeekSize
+                    + mBottomStackSlowDownHeight);
             if (scrollRange > 0) {
                 View lastChild = getLastChildNotGone();
-                if (isViewExpanded(lastChild)) {
-                    // last child is expanded, so we have to ensure that it can exit the
-                    // bottom stack
-                    scrollRange += mCollapsedSize + mPaddingBetweenElements;
-                }
                 // We want to at least be able collapse the first item and not ending in a weird
                 // end state.
                 scrollRange = Math.max(scrollRange, firstChildMaxExpandHeight - mCollapsedSize);
@@ -829,7 +987,23 @@
             int height = (int) getLayoutHeight();
             int bottom = getContentHeight();
 
-            mScroller.fling(mScrollX, mOwnScrollY, 0, velocityY, 0, 0, 0,
+            float topAmount = getCurrentOverScrollAmount(true);
+            float bottomAmount = getCurrentOverScrollAmount(false);
+            if (velocityY < 0 && topAmount > 0) {
+                mOwnScrollY -= (int) topAmount;
+                setOverScrollAmount(0, true, false);
+                mMaxOverScroll = Math.abs(velocityY) / 1000f * RUBBER_BAND_FACTOR
+                        * mOverflingDistance + topAmount;
+            } else if (velocityY > 0 && bottomAmount > 0) {
+                mOwnScrollY += bottomAmount;
+                setOverScrollAmount(0, false, false);
+                mMaxOverScroll = Math.abs(velocityY) / 1000f * RUBBER_BAND_FACTOR
+                        * mOverflingDistance + bottomAmount;
+            } else {
+                // it will be set once we reach the boundary
+                mMaxOverScroll = 0.0f;
+            }
+            mScroller.fling(mScrollX, mOwnScrollY, 1, velocityY, 0, 0, 0,
                     Math.max(0, bottom - height), 0, height/2);
 
             postInvalidateOnAnimation();
@@ -841,11 +1015,12 @@
 
         recycleVelocityTracker();
 
-        // TODO: Overscroll
-//        if (mEdgeGlowTop != null) {
-//            mEdgeGlowTop.onRelease();
-//            mEdgeGlowBottom.onRelease();
-//        }
+        if (getCurrentOverScrollAmount(true /* onTop */) > 0) {
+            setOverScrollAmount(0, true /* onTop */, true /* animate */);
+        }
+        if (getCurrentOverScrollAmount(false /* onTop */) > 0) {
+            setOverScrollAmount(0, false /* onTop */, true /* animate */);
+        }
     }
 
     @Override
@@ -1181,7 +1356,7 @@
     public int getEmptyBottomMargin() {
         int emptyMargin = mMaxLayoutHeight - mContentHeight;
         if (needsHeightAdaption()) {
-            emptyMargin = emptyMargin - mCollapsedSize - mBottomStackPeekSize;
+            emptyMargin = emptyMargin - mBottomStackSlowDownHeight - mBottomStackPeekSize;
         }
         return Math.max(emptyMargin, 0);
     }
@@ -1226,7 +1401,9 @@
      * See {@link AmbientState#setDimmed}.
      */
     public void setDimmed(boolean dimmed, boolean animate) {
+        mStackScrollAlgorithm.setDimmed(dimmed);
         mAmbientState.setDimmed(dimmed);
+        updatePadding(dimmed);
         if (animate) {
             mDimmedNeedsAnimation = true;
             mNeedsAnimation =  true;
@@ -1311,6 +1488,7 @@
 
                 // ANIMATION_TYPE_DIMMED
                 new AnimationFilter()
+                        .animateY()
                         .animateScale()
                         .animateDimmed()
         };
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/PiecewiseLinearIndentationFunctor.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/PiecewiseLinearIndentationFunctor.java
index 38b544f..1c37c35 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/PiecewiseLinearIndentationFunctor.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/PiecewiseLinearIndentationFunctor.java
@@ -38,27 +38,26 @@
      *                 the actual visual distance below the top card but is a maximum,
      *                 achieved when the next card just starts transitioning into the stack and
      *                 the stack is full.
-     *                 If totalTransitionDistance is equal to this, we directly start at the peek,
-     *                 otherwise the first element transitions between 0 and
-     *                 totalTransitionDistance - peekSize.
+     *                 If distanceToPeekStart is 0, we directly start at the peek, otherwise the
+     *                 first element transitions between 0 and distanceToPeekStart.
      *                 Visualization:
      *           ---------------------------------------------------   ---
      *          |                                                   |   |
-     *          |                  FIRST ITEM                       |   | <- totalTransitionDistance
+     *          |                  FIRST ITEM                       |   | <- distanceToPeekStart
      *          |                                                   |   |
-     *          |---------------------------------------------------|   |   ---
-     *          |__________________SECOND ITEM______________________|   |    |  <- peekSize
-     *          |===================================================|  _|_  _|_
+     *          |---------------------------------------------------|  ---  ---
+     *          |__________________SECOND ITEM______________________|        |  <- peekSize
+     *          |===================================================|       _|_
      *
-     * @param totalTransitionDistance The total transition distance an element has to go through
+     * @param distanceToPeekStart The distance to the start of the peak.
      * @param linearPart The interpolation factor between the linear and the quadratic amount taken.
      *                   This factor must be somewhere in [0 , 1]
      */
     PiecewiseLinearIndentationFunctor(int maxItemsInStack,
                                       int peekSize,
-                                      int totalTransitionDistance,
+                                      int distanceToPeekStart,
                                       float linearPart) {
-        super(maxItemsInStack, peekSize, totalTransitionDistance);
+        super(maxItemsInStack, peekSize, distanceToPeekStart);
         mBaseValues = new ArrayList<Float>(maxItemsInStack+1);
         initBaseValues();
         mLinearPart = linearPart;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackIndentationFunctor.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackIndentationFunctor.java
index f72947a..034eba6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackIndentationFunctor.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackIndentationFunctor.java
@@ -21,8 +21,8 @@
  */
 public abstract class StackIndentationFunctor {
 
-    protected final int mTotalTransitionDistance;
-    protected final int mDistanceToPeekStart;
+    protected int mTotalTransitionDistance;
+    protected int mDistanceToPeekStart;
     protected int mMaxItemsInStack;
     protected int mPeekSize;
     protected boolean mStackStartsAtPeek;
@@ -37,31 +37,41 @@
      *                 the actual visual distance below the top card but is a maximum,
      *                 achieved when the next card just starts transitioning into the stack and
      *                 the stack is full.
-     *                 If totalTransitionDistance is equal to this, we directly start at the peek,
-     *                 otherwise the first element transitions between 0 and
-     *                 totalTransitionDistance - peekSize.
+     *                 If distanceToPeekStart is 0, we directly start at the peek, otherwise the
+     *                 first element transitions between 0 and distanceToPeekStart.
      *                 Visualization:
      *           ---------------------------------------------------   ---
      *          |                                                   |   |
-     *          |                  FIRST ITEM                       |   | <- totalTransitionDistance
+     *          |                  FIRST ITEM                       |   | <- distanceToPeekStart
      *          |                                                   |   |
-     *          |---------------------------------------------------|   |   ---
-     *          |__________________SECOND ITEM______________________|   |    |  <- peekSize
-     *          |===================================================|  _|_  _|_
+     *          |---------------------------------------------------|  ---  ---
+     *          |__________________SECOND ITEM______________________|        |  <- peekSize
+     *          |===================================================|       _|_
      *
-     * @param totalTransitionDistance The total transition distance an element has to go through
+     * @param distanceToPeekStart The distance to the start of the peak.
      */
-    StackIndentationFunctor(int maxItemsInStack, int peekSize, int totalTransitionDistance) {
-        mTotalTransitionDistance = totalTransitionDistance;
-        mDistanceToPeekStart = mTotalTransitionDistance - peekSize;
+    StackIndentationFunctor(int maxItemsInStack, int peekSize, int distanceToPeekStart) {
+        mDistanceToPeekStart = distanceToPeekStart;
         mStackStartsAtPeek = mDistanceToPeekStart == 0;
         mMaxItemsInStack = maxItemsInStack;
         mPeekSize = peekSize;
+        updateTotalTransitionDistance();
 
     }
 
+    private void updateTotalTransitionDistance() {
+        mTotalTransitionDistance = mDistanceToPeekStart + mPeekSize;
+    }
+
     public void setPeekSize(int mPeekSize) {
         this.mPeekSize = mPeekSize;
+        updateTotalTransitionDistance();
+    }
+
+    public void setDistanceToPeekStart(int distanceToPeekStart) {
+        mDistanceToPeekStart = distanceToPeekStart;
+        mStackStartsAtPeek = mDistanceToPeekStart == 0;
+        updateTotalTransitionDistance();
     }
 
     /**
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 5e4d496..d572ea5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
@@ -65,13 +65,40 @@
     private ExpandableView mFirstChildWhileExpanding;
     private boolean mExpandedOnStart;
     private int mTopStackTotalSize;
+    private int mPaddingBetweenElementsDimmed;
+    private int mPaddingBetweenElementsNormal;
+    private int mBottomStackSlowDownLength;
 
     public StackScrollAlgorithm(Context context) {
         initConstants(context);
+        updatePadding(false);
+    }
+
+    private void updatePadding(boolean dimmed) {
+        mPaddingBetweenElements = dimmed
+                ? mPaddingBetweenElementsDimmed
+                : mPaddingBetweenElementsNormal;
+        mTopStackTotalSize = mCollapsedSize + mPaddingBetweenElements;
+        mTopStackIndentationFunctor = new PiecewiseLinearIndentationFunctor(
+                MAX_ITEMS_IN_TOP_STACK,
+                mTopStackPeekSize,
+                mTopStackTotalSize - mTopStackPeekSize,
+                0.5f);
+        mBottomStackIndentationFunctor = new PiecewiseLinearIndentationFunctor(
+                MAX_ITEMS_IN_BOTTOM_STACK,
+                mBottomStackPeekSize,
+                getBottomStackSlowDownLength(),
+                0.5f);
+    }
+
+    public int getBottomStackSlowDownLength() {
+        return mBottomStackSlowDownLength + mPaddingBetweenElements;
     }
 
     private void initConstants(Context context) {
-        mPaddingBetweenElements = context.getResources()
+        mPaddingBetweenElementsDimmed = context.getResources()
+                .getDimensionPixelSize(R.dimen.notification_padding_dimmed);
+        mPaddingBetweenElementsNormal = context.getResources()
                 .getDimensionPixelSize(R.dimen.notification_padding);
         mCollapsedSize = context.getResources()
                 .getDimensionPixelSize(R.dimen.notification_min_height);
@@ -82,17 +109,8 @@
         mZDistanceBetweenElements = context.getResources()
                 .getDimensionPixelSize(R.dimen.z_distance_between_notifications);
         mZBasicHeight = (MAX_ITEMS_IN_BOTTOM_STACK + 1) * mZDistanceBetweenElements;
-        mTopStackTotalSize = mCollapsedSize + mPaddingBetweenElements;
-        mTopStackIndentationFunctor = new PiecewiseLinearIndentationFunctor(
-                MAX_ITEMS_IN_TOP_STACK,
-                mTopStackPeekSize,
-                mTopStackTotalSize,
-                0.5f);
-        mBottomStackIndentationFunctor = new PiecewiseLinearIndentationFunctor(
-                MAX_ITEMS_IN_BOTTOM_STACK,
-                mBottomStackPeekSize,
-                mCollapsedSize + mBottomStackPeekSize + mPaddingBetweenElements,
-                0.5f);
+        mBottomStackSlowDownLength = context.getResources()
+                .getDimensionPixelSize(R.dimen.bottom_stack_slow_down_length);
     }
 
 
@@ -110,7 +128,10 @@
         algorithmState.scrolledPixelsTop = 0;
         algorithmState.itemsInBottomStack = 0.0f;
         algorithmState.partialInBottom = 0.0f;
-        algorithmState.scrollY = ambientState.getScrollY() + mCollapsedSize;
+        float topOverScroll = ambientState.getOverScrollAmount(true /* onTop */);
+        float bottomOverScroll = ambientState.getOverScrollAmount(false /* onTop */);
+        algorithmState.scrollY = (int) (ambientState.getScrollY() + mCollapsedSize
+                + bottomOverScroll - topOverScroll);
 
         updateVisibleChildren(resultState, algorithmState);
 
@@ -197,7 +218,6 @@
      *
      * @param resultState The result state to update if a change to the properties of a child occurs
      * @param algorithmState The state in which the current pass of the algorithm is currently in
-     *                       and which will be updated
      */
     private void updatePositionsForState(StackScrollState resultState,
             StackScrollAlgorithmState algorithmState) {
@@ -206,7 +226,7 @@
         float bottomPeekStart = mInnerHeight - mBottomStackPeekSize;
 
         // The position where the bottom stack starts.
-        float bottomStackStart = bottomPeekStart - mCollapsedSize;
+        float bottomStackStart = bottomPeekStart - mBottomStackSlowDownLength;
 
         // The y coordinate of the current child.
         float currentYPosition = 0.0f;
@@ -277,7 +297,7 @@
             // The first card is always rendered.
             if (i == 0) {
                 childViewState.alpha = 1.0f;
-                childViewState.yTranslation = 0;
+                childViewState.yTranslation = Math.max(mCollapsedSize - algorithmState.scrollY, 0);
                 childViewState.location = StackScrollState.ViewState.LOCATION_FIRST_CARD;
             }
             if (childViewState.location == StackScrollState.ViewState.LOCATION_UNKNOWN) {
@@ -352,7 +372,7 @@
         algorithmState.itemsInBottomStack += algorithmState.partialInBottom;
         childViewState.yTranslation = transitioningPositionStart + offset - childHeight
                 - mPaddingBetweenElements;
-
+        
         // We want at least to be at the end of the top stack when collapsing
         clampPositionToTopStackEnd(childViewState, childHeight);
         childViewState.location = StackScrollState.ViewState.LOCATION_MAIN_AREA;
@@ -436,7 +456,6 @@
      *
      * @param resultState The result state to update if a height change of an child occurs
      * @param algorithmState The state in which the current pass of the algorithm is currently in
-     *                       and which will be updated
      */
     private void findNumberOfItemsInTopStackAndUpdateState(StackScrollState resultState,
             StackScrollAlgorithmState algorithmState) {
@@ -454,7 +473,7 @@
                     + childHeight
                     + mPaddingBetweenElements;
             if (yPositionInScrollView < algorithmState.scrollY) {
-                if (i == 0 && algorithmState.scrollY == mCollapsedSize) {
+                if (i == 0 && algorithmState.scrollY <= mCollapsedSize) {
 
                     // The starting position of the bottom stack peek
                     int bottomPeekStart = mInnerHeight - mBottomStackPeekSize;
@@ -621,6 +640,10 @@
         }
     }
 
+    public void setDimmed(boolean dimmed) {
+        updatePadding(dimmed);
+    }
+
     class StackScrollAlgorithmState {
 
         /**
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 ca383aa..5ac51f8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
@@ -18,7 +18,6 @@
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
 import android.animation.PropertyValuesHolder;
 import android.animation.ValueAnimator;
@@ -73,6 +72,9 @@
     private AnimationFilter mAnimationFilter = new AnimationFilter();
     private long mCurrentLength;
 
+    private ValueAnimator mTopOverScrollAnimator;
+    private ValueAnimator mBottomOverScrollAnimator;
+
     public StackStateAnimator(NotificationStackScrollLayout hostLayout) {
         mHostLayout = hostLayout;
         mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(hostLayout.getContext(),
@@ -510,7 +512,7 @@
             ArrayList<NotificationStackScrollLayout.AnimationEvent> animationEvents,
             StackScrollState finalState) {
         mNewEvents.clear();
-        for (NotificationStackScrollLayout.AnimationEvent event: animationEvents) {
+        for (NotificationStackScrollLayout.AnimationEvent event : animationEvents) {
             View changingView = event.changingView;
             if (!mHandledEvents.contains(event)) {
                 if (event.animationType == NotificationStackScrollLayout.AnimationEvent
@@ -532,4 +534,34 @@
             }
         }
     }
+
+    public void animateOverScrollToAmount(float targetAmount, final boolean onTop) {
+        final float startOverScrollAmount = mHostLayout.getCurrentOverScrollAmount(onTop);
+        cancelOverScrollAnimators(onTop);
+        ValueAnimator overScrollAnimator = ValueAnimator.ofFloat(startOverScrollAmount,
+                targetAmount);
+        overScrollAnimator.setDuration(ANIMATION_DURATION_STANDARD);
+        overScrollAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+            @Override
+            public void onAnimationUpdate(ValueAnimator animation) {
+                float currentOverScroll = (float) animation.getAnimatedValue();
+                mHostLayout.setOverScrollAmount(currentOverScroll, onTop, false /* animate */,
+                        false /* cancelAnimators */);
+            }
+        });
+        overScrollAnimator.setInterpolator(mFastOutSlowInInterpolator);
+        overScrollAnimator.start();
+        if (onTop) {
+            mTopOverScrollAnimator = overScrollAnimator;
+        } else {
+            mBottomOverScrollAnimator = overScrollAnimator;
+        }
+    }
+
+    public void cancelOverScrollAnimators(boolean onTop) {
+        ValueAnimator currentAnimator = onTop ? mTopOverScrollAnimator : mBottomOverScrollAnimator;
+        if (currentAnimator != null) {
+            currentAnimator.cancel();
+        }
+    }
 }
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 3619112..36b5cfb 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -38,6 +38,7 @@
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
+import android.content.pm.UserInfo;
 import android.database.ContentObserver;
 import android.graphics.Point;
 import android.graphics.Rect;
@@ -197,6 +198,8 @@
 
     private final SparseArray<UserState> mUserStates = new SparseArray<UserState>();
 
+    private final UserManager mUserManager;
+
     private int mCurrentUserId = UserHandle.USER_OWNER;
 
     private final LongArray mTempLongArray = new LongArray();
@@ -210,15 +213,6 @@
         return getUserStateLocked(mCurrentUserId);
     }
 
-    private UserState getUserStateLocked(int userId) {
-        UserState state = mUserStates.get(userId);
-        if (state == null) {
-            state = new UserState(userId);
-            mUserStates.put(userId, state);
-        }
-        return state;
-    }
-
     /**
      * Creates a new instance.
      *
@@ -228,6 +222,7 @@
         mContext = context;
         mPackageManager = mContext.getPackageManager();
         mWindowManagerService = LocalServices.getService(WindowManagerInternal.class);
+        mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
         mSecurityPolicy = new SecurityPolicy();
         mMainHandler = new MainHandler(mContext.getMainLooper());
         registerBroadcastReceivers();
@@ -235,11 +230,22 @@
                 context.getContentResolver());
     }
 
+    private UserState getUserStateLocked(int userId) {
+        UserState state = mUserStates.get(userId);
+        if (state == null) {
+            state = new UserState(userId);
+            mUserStates.put(userId, state);
+        }
+        return state;
+    }
+
     private void registerBroadcastReceivers() {
         PackageMonitor monitor = new PackageMonitor() {
             @Override
             public void onSomePackagesChanged() {
                 synchronized (mLock) {
+                    // Only the profile parent can install accessibility services.
+                    // Therefore we ignore packages from linked profiles.
                     if (getChangingUserId() != mCurrentUserId) {
                         return;
                     }
@@ -262,6 +268,8 @@
             public void onPackageRemoved(String packageName, int uid) {
                 synchronized (mLock) {
                     final int userId = getChangingUserId();
+                    // Only the profile parent can install accessibility services.
+                    // Therefore we ignore packages from linked profiles.
                     if (userId != mCurrentUserId) {
                         return;
                     }
@@ -297,6 +305,8 @@
                     int uid, boolean doit) {
                 synchronized (mLock) {
                     final int userId = getChangingUserId();
+                    // Only the profile parent can install accessibility services.
+                    // Therefore we ignore packages from linked profiles.
                     if (userId != mCurrentUserId) {
                         return false;
                     }
@@ -359,6 +369,9 @@
     @Override
     public int addClient(IAccessibilityManagerClient client, int userId) {
         synchronized (mLock) {
+            // We treat calls from a profile as if made by its parent as profiles
+            // share the accessibility state of the parent. The call below
+            // performs the current profile parent resolution.
             final int resolvedUserId = mSecurityPolicy
                     .resolveCallingUserIdEnforcingPermissionsLocked(userId);
             // If the client is from a process that runs across users such as
@@ -388,6 +401,9 @@
     @Override
     public boolean sendAccessibilityEvent(AccessibilityEvent event, int userId) {
         synchronized (mLock) {
+            // We treat calls from a profile as if made by its parent as profiles
+            // share the accessibility state of the parent. The call below
+            // performs the current profile parent resolution..
             final int resolvedUserId = mSecurityPolicy
                     .resolveCallingUserIdEnforcingPermissionsLocked(userId);
             // This method does nothing for a background user.
@@ -414,6 +430,9 @@
     @Override
     public List<AccessibilityServiceInfo> getInstalledAccessibilityServiceList(int userId) {
         synchronized (mLock) {
+            // We treat calls from a profile as if made by its parent as profiles
+            // share the accessibility state of the parent. The call below
+            // performs the current profile parent resolution.
             final int resolvedUserId = mSecurityPolicy
                     .resolveCallingUserIdEnforcingPermissionsLocked(userId);
             // The automation service is a fake one and should not be reported
@@ -435,6 +454,9 @@
             int userId) {
         List<AccessibilityServiceInfo> result = null;
         synchronized (mLock) {
+            // We treat calls from a profile as if made by its parent as profiles
+            // share the accessibility state of the parent. The call below
+            // performs the current profile parent resolution.
             final int resolvedUserId = mSecurityPolicy
                     .resolveCallingUserIdEnforcingPermissionsLocked(userId);
 
@@ -468,6 +490,9 @@
     public void interrupt(int userId) {
         CopyOnWriteArrayList<Service> services;
         synchronized (mLock) {
+            // We treat calls from a profile as if made by its parent as profiles
+            // share the accessibility state of the parent. The call below
+            // performs the current profile parent resolution.
             final int resolvedUserId = mSecurityPolicy
                     .resolveCallingUserIdEnforcingPermissionsLocked(userId);
             // This method does nothing for a background user.
@@ -491,6 +516,9 @@
     public int addAccessibilityInteractionConnection(IWindow windowToken,
             IAccessibilityInteractionConnection connection, int userId) throws RemoteException {
         synchronized (mLock) {
+            // We treat calls from a profile as if made by its parent as profiles
+            // share the accessibility state of the parent. The call below
+            // performs the current profile parent resolution.
             final int resolvedUserId = mSecurityPolicy
                     .resolveCallingUserIdEnforcingPermissionsLocked(userId);
             final int windowId = sNextWindowId++;
@@ -527,6 +555,9 @@
     @Override
     public void removeAccessibilityInteractionConnection(IWindow window) {
         synchronized (mLock) {
+            // We treat calls from a profile as if made by its parent as profiles
+            // share the accessibility state of the parent. The call below
+            // performs the current profile parent resolution.
             mSecurityPolicy.resolveCallingUserIdEnforcingPermissionsLocked(
                     UserHandle.getCallingUserId());
             IBinder token = window.asBinder();
@@ -675,6 +706,9 @@
                 Manifest.permission.RETRIEVE_WINDOW_TOKEN,
                 GET_WINDOW_TOKEN);
         synchronized (mLock) {
+            // We treat calls from a profile as if made by its parent as profiles
+            // share the accessibility state of the parent. The call below
+            // performs the current profile parent resolution.
             final int resolvedUserId = mSecurityPolicy
                     .resolveCallingUserIdEnforcingPermissionsLocked(
                             UserHandle.getCallingUserId());
@@ -770,7 +804,7 @@
             }
 
             // Disconnect from services for the old user.
-            UserState oldUserState = getUserStateLocked(mCurrentUserId);
+            UserState oldUserState = getCurrentUserStateLocked();
             oldUserState.onSwitchToAnotherUser();
 
             // Disable the local managers for the old user.
@@ -2034,6 +2068,9 @@
         @Override
         public List<AccessibilityWindowInfo> getWindows() {
             synchronized (mLock) {
+                // We treat calls from a profile as if made by its perent as profiles
+                // share the accessibility state of the parent. The call below
+                // performs the current profile parent resolution.
                 final int resolvedUserId = mSecurityPolicy
                         .resolveCallingUserIdEnforcingPermissionsLocked(
                                 UserHandle.getCallingUserId());
@@ -2062,6 +2099,9 @@
         @Override
         public AccessibilityWindowInfo getWindow(int windowId) {
             synchronized (mLock) {
+                // We treat calls from a profile as if made by its parent as profiles
+                // share the accessibility state of the parent. The call below
+                // performs the current profile parent resolution.
                 final int resolvedUserId = mSecurityPolicy
                         .resolveCallingUserIdEnforcingPermissionsLocked(
                                 UserHandle.getCallingUserId());
@@ -2092,6 +2132,9 @@
             final int resolvedWindowId;
             IAccessibilityInteractionConnection connection = null;
             synchronized (mLock) {
+                // We treat calls from a profile as if made by its parent as profiles
+                // share the accessibility state of the parent. The call below
+                // performs the current profile parent resolution.
                 final int resolvedUserId = mSecurityPolicy
                         .resolveCallingUserIdEnforcingPermissionsLocked(
                                 UserHandle.getCallingUserId());
@@ -2136,9 +2179,12 @@
             final int resolvedWindowId;
             IAccessibilityInteractionConnection connection = null;
             synchronized (mLock) {
+                // We treat calls from a profile as if made by its parent as profiles
+                // share the accessibility state of the parent. The call below
+                // performs the current profile parent resolution.
                 final int resolvedUserId = mSecurityPolicy
                         .resolveCallingUserIdEnforcingPermissionsLocked(
-                        UserHandle.getCallingUserId());
+                                UserHandle.getCallingUserId());
                 if (resolvedUserId != mCurrentUserId) {
                     return false;
                 }
@@ -2180,9 +2226,12 @@
             final int resolvedWindowId;
             IAccessibilityInteractionConnection connection = null;
             synchronized (mLock) {
+                // We treat calls from a profile as if made by its parent as profiles
+                // share the accessibility state of the parent. The call below
+                // performs the current profile parent resolution.
                 final int resolvedUserId = mSecurityPolicy
                         .resolveCallingUserIdEnforcingPermissionsLocked(
-                        UserHandle.getCallingUserId());
+                                UserHandle.getCallingUserId());
                 if (resolvedUserId != mCurrentUserId) {
                     return false;
                 }
@@ -2224,9 +2273,12 @@
             final int resolvedWindowId;
             IAccessibilityInteractionConnection connection = null;
             synchronized (mLock) {
+                // We treat calls from a profile as if made by its parent as profiles
+                // share the accessibility state of the parent. The call below
+                // performs the current profile parent resolution.
                 final int resolvedUserId = mSecurityPolicy
                         .resolveCallingUserIdEnforcingPermissionsLocked(
-                        UserHandle.getCallingUserId());
+                                UserHandle.getCallingUserId());
                 if (resolvedUserId != mCurrentUserId) {
                     return false;
                 }
@@ -2268,9 +2320,12 @@
             final int resolvedWindowId;
             IAccessibilityInteractionConnection connection = null;
             synchronized (mLock) {
+                // We treat calls from a profile as if made by its parent as profiles
+                // share the accessibility state of the parent. The call below
+                // performs the current profile parent resolution.
                 final int resolvedUserId = mSecurityPolicy
                         .resolveCallingUserIdEnforcingPermissionsLocked(
-                        UserHandle.getCallingUserId());
+                                UserHandle.getCallingUserId());
                 if (resolvedUserId != mCurrentUserId) {
                     return false;
                 }
@@ -2311,9 +2366,12 @@
             final int resolvedWindowId;
             IAccessibilityInteractionConnection connection = null;
             synchronized (mLock) {
+                // We treat calls from a profile as if made by its parent as profiles
+                // share the accessibility state of the parent. The call below
+                // performs the current profile parent resolution.
                 final int resolvedUserId = mSecurityPolicy
                         .resolveCallingUserIdEnforcingPermissionsLocked(
-                        UserHandle.getCallingUserId());
+                                UserHandle.getCallingUserId());
                 if (resolvedUserId != mCurrentUserId) {
                     return false;
                 }
@@ -2346,9 +2404,12 @@
 
         public boolean performGlobalAction(int action) {
             synchronized (mLock) {
+                // We treat calls from a profile as if made by its parent as profiles
+                // share the accessibility state of the parent. The call below
+                // performs the current profile parent resolution.
                 final int resolvedUserId = mSecurityPolicy
                         .resolveCallingUserIdEnforcingPermissionsLocked(
-                        UserHandle.getCallingUserId());
+                                UserHandle.getCallingUserId());
                 if (resolvedUserId != mCurrentUserId) {
                     return false;
                 }
@@ -3407,16 +3468,35 @@
                     & AccessibilityServiceInfo.CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT) != 0;
         }
 
+        private int resolveProfileParentLocked(int userId) {
+            if (userId != mCurrentUserId) {
+                final long identity = Binder.clearCallingIdentity();
+                try {
+                    UserInfo parent = mUserManager.getProfileParent(userId);
+                    if (parent != null) {
+                        return parent.getUserHandle().getIdentifier();
+                    }
+                } finally {
+                    Binder.restoreCallingIdentity(identity);
+                }
+            }
+            return userId;
+        }
+
         public int resolveCallingUserIdEnforcingPermissionsLocked(int userId) {
             final int callingUid = Binder.getCallingUid();
             if (callingUid == 0
                     || callingUid == Process.SYSTEM_UID
                     || callingUid == Process.SHELL_UID) {
-                return mCurrentUserId;
+                if (userId == UserHandle.USER_CURRENT
+                        || userId == UserHandle.USER_CURRENT_OR_SELF) {
+                    return mCurrentUserId;
+                }
+                return resolveProfileParentLocked(userId);
             }
             final int callingUserId = UserHandle.getUserId(callingUid);
             if (callingUserId == userId) {
-                return userId;
+                return resolveProfileParentLocked(userId);
             }
             if (!hasPermission(Manifest.permission.INTERACT_ACROSS_USERS)
                     && !hasPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL)) {
@@ -3618,17 +3698,8 @@
         private final Uri mEnhancedWebAccessibilityUri = Settings.Secure
                 .getUriFor(Settings.Secure.ACCESSIBILITY_SCRIPT_INJECTION);
 
-        private final Uri mDisplayContrastEnabledUri = Settings.Secure.getUriFor(
-                Settings.Secure.ACCESSIBILITY_DISPLAY_CONTRAST_ENABLED);
-        private final Uri mDisplayContrastUri = Settings.Secure.getUriFor(
-                Settings.Secure.ACCESSIBILITY_DISPLAY_CONTRAST);
-        private final Uri mDisplayBrightnessUri = Settings.Secure.getUriFor(
-                Settings.Secure.ACCESSIBILITY_DISPLAY_BRIGHTNESS);
-
         private final Uri mDisplayInversionEnabledUri = Settings.Secure.getUriFor(
                 Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED);
-        private final Uri mDisplayInversionUri = Settings.Secure.getUriFor(
-                Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION);
 
         private final Uri mDisplayDaltonizerEnabledUri = Settings.Secure.getUriFor(
                 Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED);
@@ -3654,16 +3725,8 @@
             contentResolver.registerContentObserver(mEnhancedWebAccessibilityUri,
                     false, this, UserHandle.USER_ALL);
             contentResolver.registerContentObserver(
-                    mDisplayContrastEnabledUri, false, this, UserHandle.USER_ALL);
-            contentResolver.registerContentObserver(
-                    mDisplayContrastUri, false, this, UserHandle.USER_ALL);
-            contentResolver.registerContentObserver(
-                    mDisplayBrightnessUri, false, this, UserHandle.USER_ALL);
-            contentResolver.registerContentObserver(
                     mDisplayInversionEnabledUri, false, this, UserHandle.USER_ALL);
             contentResolver.registerContentObserver(
-                    mDisplayInversionUri, false, this, UserHandle.USER_ALL);
-            contentResolver.registerContentObserver(
                     mDisplayDaltonizerEnabledUri, false, this, UserHandle.USER_ALL);
             contentResolver.registerContentObserver(
                     mDisplayDaltonizerUri, false, this, UserHandle.USER_ALL);
@@ -3673,8 +3736,10 @@
         public void onChange(boolean selfChange, Uri uri) {
             if (mAccessibilityEnabledUri.equals(uri)) {
                 synchronized (mLock) {
-                    // We will update when the automation service dies.
+                    // Profiles share the accessibility state of the parent. Therefore,
+                    // we are checking for changes only the parent settings.
                     UserState userState = getCurrentUserStateLocked();
+                    // We will update when the automation service dies.
                     if (userState.mUiAutomationService == null) {
                         if (readAccessibilityEnabledSettingLocked(userState)) {
                             onUserStateChangedLocked(userState);
@@ -3683,8 +3748,10 @@
                 }
             } else if (mTouchExplorationEnabledUri.equals(uri)) {
                 synchronized (mLock) {
-                    // We will update when the automation service dies.
+                    // Profiles share the accessibility state of the parent. Therefore,
+                    // we are checking for changes only the parent settings.
                     UserState userState = getCurrentUserStateLocked();
+                    // We will update when the automation service dies.
                     if (userState.mUiAutomationService == null) {
                         if (readTouchExplorationEnabledSettingLocked(userState)) {
                             onUserStateChangedLocked(userState);
@@ -3693,8 +3760,10 @@
                 }
             } else if (mDisplayMagnificationEnabledUri.equals(uri)) {
                 synchronized (mLock) {
-                    // We will update when the automation service dies.
+                    // Profiles share the accessibility state of the parent. Therefore,
+                    // we are checking for changes only the parent settings.
                     UserState userState = getCurrentUserStateLocked();
+                    // We will update when the automation service dies.
                     if (userState.mUiAutomationService == null) {
                         if (readDisplayMagnificationEnabledSettingLocked(userState)) {
                             onUserStateChangedLocked(userState);
@@ -3703,8 +3772,10 @@
                 }
             } else if (mEnabledAccessibilityServicesUri.equals(uri)) {
                 synchronized (mLock) {
-                    // We will update when the automation service dies.
+                    // Profiles share the accessibility state of the parent. Therefore,
+                    // we are checking for changes only the parent settings.
                     UserState userState = getCurrentUserStateLocked();
+                    // We will update when the automation service dies.
                     if (userState.mUiAutomationService == null) {
                         if (readEnabledAccessibilityServicesLocked(userState)) {
                             onUserStateChangedLocked(userState);
@@ -3713,8 +3784,10 @@
                 }
             } else if (mTouchExplorationGrantedAccessibilityServicesUri.equals(uri)) {
                 synchronized (mLock) {
-                    // We will update when the automation service dies.
+                    // Profiles share the accessibility state of the parent. Therefore,
+                    // we are checking for changes only the parent settings.
                     UserState userState = getCurrentUserStateLocked();
+                    // We will update when the automation service dies.
                     if (userState.mUiAutomationService == null) {
                         if (readTouchExplorationGrantedAccessibilityServicesLocked(userState)) {
                             onUserStateChangedLocked(userState);
@@ -3723,24 +3796,24 @@
                 }
             } else if (mEnhancedWebAccessibilityUri.equals(uri)) {
                 synchronized (mLock) {
-                    // We will update when the automation service dies.
+                    // Profiles share the accessibility state of the parent. Therefore,
+                    // we are checking for changes only the parent settings.
                     UserState userState = getCurrentUserStateLocked();
+                    // We will update when the automation service dies.
                     if (userState.mUiAutomationService == null) {
                         if (readEnhancedWebAccessibilityEnabledChangedLocked(userState)) {
                             onUserStateChangedLocked(userState);
                         }
                     }
                 }
-            } else if (mDisplayContrastEnabledUri.equals(uri)
-                    || mDisplayInversionEnabledUri.equals(uri)
+            } else if (mDisplayInversionEnabledUri.equals(uri)
                     || mDisplayDaltonizerEnabledUri.equals(uri)
-                    || mDisplayContrastUri.equals(uri)
-                    || mDisplayBrightnessUri.equals(uri)
-                    || mDisplayInversionUri.equals(uri)
                     || mDisplayDaltonizerUri.equals(uri)) {
                 synchronized (mLock) {
-                    // We will update when the automation service dies.
+                    // Profiles share the accessibility state of the parent. Therefore,
+                    // we are checking for changes only the parent settings.
                     UserState userState = getCurrentUserStateLocked();
+                    // We will update when the automation service dies.
                     if (userState.mUiAutomationService == null) {
                         if (readDisplayColorAdjustmentSettingsLocked(userState)) {
                             updateDisplayColorAdjustmentSettingsLocked(userState);
diff --git a/services/accessibility/java/com/android/server/accessibility/DisplayAdjustmentUtils.java b/services/accessibility/java/com/android/server/accessibility/DisplayAdjustmentUtils.java
index 52bdeda..394c196 100644
--- a/services/accessibility/java/com/android/server/accessibility/DisplayAdjustmentUtils.java
+++ b/services/accessibility/java/com/android/server/accessibility/DisplayAdjustmentUtils.java
@@ -41,22 +41,6 @@
              0,      0,      0, 1
     };
 
-    /** Matrix and offset used for standard display inversion. */
-    private static final float[] INVERSION_MATRIX_STANDARD = new float[] {
-        -1,  0,  0, 0,
-         0, -1,  0, 0,
-         0,  0, -1, 0,
-         1,  1,  1, 1
-    };
-
-    /** Matrix and offset used for hue-only display inversion. */
-    private static final float[] INVERSION_MATRIX_HUE_ONLY = new float[] {
-          0, .5f, .5f, 0,
-        .5f,   0, .5f, 0,
-        .5f, .5f,   0, 0,
-          0,   0,   0, 1
-    };
-
     /** Matrix and offset used for value-only display inversion. */
     private static final float[] INVERSION_MATRIX_VALUE_ONLY = new float[] {
            0, -.5f, -.5f, 0,
@@ -65,15 +49,6 @@
            1,    1,    1, 1
     };
 
-    /** Default contrast for display contrast enhancement. */
-    private static final float DEFAULT_DISPLAY_CONTRAST = 2;
-
-    /** Default brightness for display contrast enhancement. */
-    private static final float DEFAULT_DISPLAY_BRIGHTNESS = 0;
-
-    /** Default inversion mode for display color inversion. */
-    private static final int DEFAULT_DISPLAY_INVERSION = AccessibilityManager.INVERSION_STANDARD;
-
     /** Default inversion mode for display color correction. */
     private static final int DEFAULT_DISPLAY_DALTONIZER =
             AccessibilityManager.DALTONIZER_CORRECT_DEUTERANOMALY;
@@ -90,11 +65,6 @@
 
         if (!hasColorTransform) {
             hasColorTransform |= Settings.Secure.getIntForUser(
-                cr, Settings.Secure.ACCESSIBILITY_DISPLAY_CONTRAST_ENABLED, 0, userId) == 1;
-        }
-
-        if (!hasColorTransform) {
-            hasColorTransform |= Settings.Secure.getIntForUser(
                 cr, Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED, 0, userId) == 1;
         }
 
@@ -115,21 +85,7 @@
         final boolean inversionEnabled = Settings.Secure.getIntForUser(
                 cr, Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED, 0, userId) == 1;
         if (inversionEnabled) {
-            final int inversionMode = Settings.Secure.getIntForUser(cr,
-                    Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION, DEFAULT_DISPLAY_INVERSION,
-                    userId);
-            final float[] inversionMatrix;
-            switch (inversionMode) {
-                case AccessibilityManager.INVERSION_HUE_ONLY:
-                    inversionMatrix = INVERSION_MATRIX_HUE_ONLY;
-                    break;
-                case AccessibilityManager.INVERSION_VALUE_ONLY:
-                    inversionMatrix = INVERSION_MATRIX_VALUE_ONLY;
-                    break;
-                default:
-                    inversionMatrix = INVERSION_MATRIX_STANDARD;
-            }
-
+            final float[] inversionMatrix = INVERSION_MATRIX_VALUE_ONLY;
             Matrix.multiplyMM(outputMatrix, 0, colorMatrix, 0, inversionMatrix, 0);
 
             colorMatrix = outputMatrix;
@@ -138,31 +94,6 @@
             hasColorTransform = true;
         }
 
-        final boolean contrastEnabled = Settings.Secure.getIntForUser(
-                cr, Settings.Secure.ACCESSIBILITY_DISPLAY_CONTRAST_ENABLED, 0, userId) == 1;
-        if (contrastEnabled) {
-            final float contrast = Settings.Secure.getFloatForUser(cr,
-                    Settings.Secure.ACCESSIBILITY_DISPLAY_CONTRAST, DEFAULT_DISPLAY_CONTRAST,
-                    userId);
-            final float brightness = Settings.Secure.getFloatForUser(cr,
-                    Settings.Secure.ACCESSIBILITY_DISPLAY_BRIGHTNESS, DEFAULT_DISPLAY_BRIGHTNESS,
-                    userId);
-            final float off = brightness * contrast - 0.5f * contrast + 0.5f;
-            final float[] contrastMatrix = {
-                    contrast, 0, 0, 0,
-                    0, contrast, 0, 0,
-                    0, 0, contrast, 0,
-                    off, off, off, 1
-            };
-
-            Matrix.multiplyMM(outputMatrix, 0, colorMatrix, 0, contrastMatrix, 0);
-
-            colorMatrix = outputMatrix;
-            outputMatrix = colorMatrix;
-
-            hasColorTransform = true;
-        }
-
         final boolean daltonizerEnabled = Settings.Secure.getIntForUser(
                 cr, Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED, 0, userId) != 0;
         if (daltonizerEnabled) {
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index dfffa8a..ce2aefb 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -65,6 +65,7 @@
 import android.net.LinkProperties.CompareResult;
 import android.net.LinkQualityInfo;
 import android.net.MobileDataStateTracker;
+import android.net.Network;
 import android.net.NetworkConfig;
 import android.net.NetworkInfo;
 import android.net.NetworkInfo.DetailedState;
@@ -165,6 +166,8 @@
 import javax.net.ssl.HttpsURLConnection;
 import javax.net.ssl.SSLSession;
 
+import static android.net.ConnectivityManager.INVALID_NET_ID;
+
 /**
  * @hide
  */
@@ -442,6 +445,10 @@
 
     TelephonyManager mTelephonyManager;
 
+    private final static int MIN_NET_ID = 10; // some reserved marks
+    private final static int MAX_NET_ID = 65535;
+    private int mNextNetId = MIN_NET_ID;
+
     public ConnectivityService(Context context, INetworkManagementService netd,
             INetworkStatsService statsService, INetworkPolicyManager policyManager) {
         // Currently, omitting a NetworkFactory will create one internally
@@ -706,6 +713,12 @@
         mAppOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
     }
 
+    private synchronized int nextNetId() {
+        int netId = mNextNetId;
+        if (++mNextNetId > MAX_NET_ID) mNextNetId = MIN_NET_ID;
+        return netId;
+    }
+
     /**
      * Factory that creates {@link NetworkStateTracker} instances using given
      * {@link NetworkConfig}.
@@ -1104,7 +1117,7 @@
                 if (tracker != null) {
                     final NetworkInfo info = getFilteredNetworkInfo(tracker, uid);
                     result.add(new NetworkState(
-                            info, tracker.getLinkProperties(), tracker.getLinkCapabilities()));
+                            info, tracker.getLinkProperties(), tracker.getNetworkCapabilities()));
                 }
             }
         }
@@ -1116,7 +1129,7 @@
             final NetworkStateTracker tracker = mNetTrackers[networkType];
             if (tracker != null) {
                 return new NetworkState(tracker.getNetworkInfo(), tracker.getLinkProperties(),
-                        tracker.getLinkCapabilities());
+                        tracker.getNetworkCapabilities());
             }
         }
         return null;
@@ -1984,6 +1997,7 @@
         int prevNetType = info.getType();
 
         mNetTrackers[prevNetType].setTeardownRequested(false);
+        int thisNetId = mNetTrackers[prevNetType].getNetwork().netId;
 
         // Remove idletimer previously setup in {@code handleConnect}
         if (mNetConfigs[prevNetType].isDefault()) {
@@ -2069,6 +2083,13 @@
             sendConnectedBroadcastDelayed(mNetTrackers[mActiveDefaultNetwork].getNetworkInfo(),
                     getConnectivityChangeDelay());
         }
+        try {
+//            mNetd.removeNetwork(thisNetId);
+        } catch (Exception e) {
+            loge("Exception removing network: " + e);
+        } finally {
+            mNetTrackers[prevNetType].setNetId(INVALID_NET_ID);
+        }
     }
 
     private void tryFailover(int prevNetType) {
@@ -2336,17 +2357,23 @@
         if (mNetConfigs[newNetType].isDefault()) {
             if (mActiveDefaultNetwork != -1 && mActiveDefaultNetwork != newNetType) {
                 if (isNewNetTypePreferredOverCurrentNetType(newNetType)) {
-                    // tear down the other
-                    NetworkStateTracker otherNet =
-                            mNetTrackers[mActiveDefaultNetwork];
-                    if (DBG) {
-                        log("Policy requires " + otherNet.getNetworkInfo().getTypeName() +
-                            " teardown");
-                    }
-                    if (!teardown(otherNet)) {
-                        loge("Network declined teardown request");
-                        teardown(thisNet);
-                        return;
+                   String teardownPolicy = SystemProperties.get("net.teardownPolicy");
+                   if (TextUtils.equals(teardownPolicy, "keep") == false) {
+                        // tear down the other
+                        NetworkStateTracker otherNet =
+                                mNetTrackers[mActiveDefaultNetwork];
+                        if (DBG) {
+                            log("Policy requires " + otherNet.getNetworkInfo().getTypeName() +
+                                " teardown");
+                        }
+                        if (!teardown(otherNet)) {
+                            loge("Network declined teardown request");
+                            teardown(thisNet);
+                            return;
+                        }
+                    } else {
+                        //TODO - remove
+                        loge("network teardown skipped due to net.teardownPolicy setting");
                     }
                 } else {
                        // don't accept this one
@@ -2358,6 +2385,15 @@
                         return;
                 }
             }
+            int thisNetId = nextNetId();
+            thisNet.setNetId(thisNetId);
+            try {
+//                mNetd.createNetwork(thisNetId, thisIface);
+            } catch (Exception e) {
+                loge("Exception creating network :" + e);
+                teardown(thisNet);
+                return;
+            }
             setupDataActivityTracking(newNetType);
             synchronized (ConnectivityService.this) {
                 // have a new default network, release the transition wakelock in a second
@@ -2380,6 +2416,16 @@
             // Don't do this - if we never sign in stay, grey
             //reportNetworkCondition(mActiveDefaultNetwork, 100);
             updateNetworkSettings(thisNet);
+        } else {
+            int thisNetId = nextNetId();
+            thisNet.setNetId(thisNetId);
+            try {
+//                mNetd.createNetwork(thisNetId, thisIface);
+            } catch (Exception e) {
+                loge("Exception creating network :" + e);
+                teardown(thisNet);
+                return;
+            }
         }
         thisNet.setTeardownRequested(false);
         updateMtuSizeSettings(thisNet);
diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java
index 10315a7..0b9570d 100644
--- a/services/core/java/com/android/server/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/InputMethodManagerService.java
@@ -1464,6 +1464,7 @@
 
     private boolean needsToShowImeSwitchOngoingNotification() {
         if (!mShowOngoingImeSwitcherForPhones) return false;
+        if (mSwitchingDialog != null) return false;
         if (isScreenLocked()) return false;
         synchronized (mMethodMap) {
             List<InputMethodInfo> imis = mSettings.getEnabledInputMethodListLocked();
@@ -2812,6 +2813,7 @@
             mSwitchingDialog.getWindow().getAttributes().privateFlags |=
                     WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
             mSwitchingDialog.getWindow().getAttributes().setTitle("Select input method");
+            updateImeWindowStatusLocked();
             mSwitchingDialog.show();
         }
     }
@@ -2869,6 +2871,7 @@
             mSwitchingDialog = null;
         }
 
+        updateImeWindowStatusLocked();
         mDialogBuilder = null;
         mIms = null;
     }
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index d6ecb46..e54e5d0 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -427,7 +427,7 @@
         }
 
         // bind to fused provider if supported
-        if (FlpHardwareProvider.isSupported()) {
+        if (FlpHardwareProvider.getInstance(mContext).isSupported()) {
           FlpHardwareProvider flpHardwareProvider = FlpHardwareProvider.getInstance(mContext);
           FusedProxy fusedProxy = FusedProxy.createAndBind(
                   mContext,
diff --git a/services/core/java/com/android/server/NativeDaemonConnector.java b/services/core/java/com/android/server/NativeDaemonConnector.java
index 0d1e122..96f9ab0 100644
--- a/services/core/java/com/android/server/NativeDaemonConnector.java
+++ b/services/core/java/com/android/server/NativeDaemonConnector.java
@@ -50,6 +50,8 @@
 final class NativeDaemonConnector implements Runnable, Handler.Callback, Watchdog.Monitor {
     private static final boolean LOGD = false;
 
+    private final static boolean VDBG = false;
+
     private final String TAG;
 
     private String mSocket;
@@ -409,7 +411,7 @@
                 loge("timed-out waiting for response to " + logCmd);
                 throw new NativeDaemonFailureException(logCmd, event);
             }
-            log("RMV <- {" + event + "}");
+            if (VDBG) log("RMV <- {" + event + "}");
             events.add(event);
         } while (event.isClassContinue());
 
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index 7ce45f7..b9c86dc 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -2029,4 +2029,24 @@
 
         pw.print("Firewall enabled: "); pw.println(mFirewallEnabled);
     }
+
+    public void createNetwork(int netId, String iface) {
+        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+
+        try {
+            mConnector.execute("network", "create", netId, iface);
+        } catch (NativeDaemonConnectorException e) {
+            throw e.rethrowAsParcelableException();
+        }
+    }
+
+    public void removeNetwork(int netId) {
+        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+
+        try {
+            mConnector.execute("network", "destroy", netId);
+        } catch (NativeDaemonConnectorException e) {
+            throw e.rethrowAsParcelableException();
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/NetworkScoreService.java b/services/core/java/com/android/server/NetworkScoreService.java
index 4f0c9b5..512ebc6 100644
--- a/services/core/java/com/android/server/NetworkScoreService.java
+++ b/services/core/java/com/android/server/NetworkScoreService.java
@@ -171,6 +171,7 @@
             return;
         }
         writer.println("Current scorer: " + currentScorer);
+        writer.flush();
 
         for (INetworkScoreCache scoreCache : getScoreCaches()) {
             try {
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index d4565b6..cfaf016 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -22,8 +22,8 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.PackageManager;
-import android.net.LinkCapabilities;
 import android.net.LinkProperties;
+import android.net.NetworkCapabilities;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.Handler;
@@ -120,7 +120,7 @@
 
     private LinkProperties mDataConnectionLinkProperties;
 
-    private LinkCapabilities mDataConnectionLinkCapabilities;
+    private NetworkCapabilities mDataConnectionNetworkCapabilities;
 
     private Bundle mCellLocation = new Bundle();
 
@@ -553,7 +553,7 @@
 
     public void notifyDataConnection(int state, boolean isDataConnectivityPossible,
             String reason, String apn, String apnType, LinkProperties linkProperties,
-            LinkCapabilities linkCapabilities, int networkType, boolean roaming) {
+            NetworkCapabilities networkCapabilities, int networkType, boolean roaming) {
         if (!checkNotifyPermission("notifyDataConnection()" )) {
             return;
         }
@@ -587,7 +587,7 @@
             mDataConnectionPossible = isDataConnectivityPossible;
             mDataConnectionReason = reason;
             mDataConnectionLinkProperties = linkProperties;
-            mDataConnectionLinkCapabilities = linkCapabilities;
+            mDataConnectionNetworkCapabilities = networkCapabilities;
             if (mDataConnectionNetworkType != networkType) {
                 mDataConnectionNetworkType = networkType;
                 // need to tell registered listeners about the new network type
@@ -624,7 +624,7 @@
             handleRemoveListLocked();
         }
         broadcastDataConnectionStateChanged(state, isDataConnectivityPossible, reason, apn,
-                apnType, linkProperties, linkCapabilities, roaming);
+                apnType, linkProperties, networkCapabilities, roaming);
         broadcastPreciseDataConnectionStateChanged(state, networkType, apnType, apn, reason,
                 linkProperties, "");
     }
@@ -794,7 +794,8 @@
             pw.println("  mDataConnectionReason=" + mDataConnectionReason);
             pw.println("  mDataConnectionApn=" + mDataConnectionApn);
             pw.println("  mDataConnectionLinkProperties=" + mDataConnectionLinkProperties);
-            pw.println("  mDataConnectionLinkCapabilities=" + mDataConnectionLinkCapabilities);
+            pw.println("  mDataConnectionNetworkCapabilities=" +
+                    mDataConnectionNetworkCapabilities);
             pw.println("  mCellLocation=" + mCellLocation);
             pw.println("  mCellInfo=" + mCellInfo);
             pw.println("  mDcRtInfo=" + mDcRtInfo);
@@ -862,7 +863,7 @@
     private void broadcastDataConnectionStateChanged(int state,
             boolean isDataConnectivityPossible,
             String reason, String apn, String apnType, LinkProperties linkProperties,
-            LinkCapabilities linkCapabilities, boolean roaming) {
+            NetworkCapabilities networkCapabilities, boolean roaming) {
         // Note: not reporting to the battery stats service here, because the
         // status bar takes care of that after taking into account all of the
         // required info.
@@ -882,8 +883,8 @@
                 intent.putExtra(PhoneConstants.DATA_IFACE_NAME_KEY, iface);
             }
         }
-        if (linkCapabilities != null) {
-            intent.putExtra(PhoneConstants.DATA_LINK_CAPABILITIES_KEY, linkCapabilities);
+        if (networkCapabilities != null) {
+            intent.putExtra(PhoneConstants.DATA_NETWORK_CAPABILITIES_KEY, networkCapabilities);
         }
         if (roaming) intent.putExtra(PhoneConstants.DATA_NETWORK_ROAMING_KEY, true);
 
diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java
index ea9de1e..82c13e0 100644
--- a/services/core/java/com/android/server/VibratorService.java
+++ b/services/core/java/com/android/server/VibratorService.java
@@ -360,8 +360,9 @@
     // Lock held on mVibrations
     private void startVibrationLocked(final Vibration vib) {
         try {
-            if (mLowPowerMode && vib.mStreamHint != AudioManager.STREAM_RING)
-                    return;
+            if (mLowPowerMode && vib.mStreamHint != AudioManager.STREAM_RING) {
+                return;
+            }
 
             int mode = mAppOpsService.checkAudioOperation(AppOpsManager.OP_VIBRATE,
                     vib.mStreamHint, vib.mUid, vib.mOpPkg);
@@ -443,7 +444,7 @@
                 }
 
                 mLowPowerMode = Settings.Global.getInt(mContext.getContentResolver(),
-                     Settings.Global.LOW_POWER_MODE, 0) != 0;
+                         Settings.Global.LOW_POWER_MODE, 0) != 0;
 
                 if (mVibrateInputDevicesSetting) {
                     if (!mInputDeviceListenerRegistered) {
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index f587ccc..b2aaf74 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -34,6 +34,7 @@
 import android.app.Notification;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
+import android.app.admin.DevicePolicyManager;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.ContentValues;
@@ -857,7 +858,7 @@
         checkManageAccountsPermission();
         UserHandle user = Binder.getCallingUserHandle();
         UserAccounts accounts = getUserAccountsForCaller();
-        if (!canUserModifyAccounts(Binder.getCallingUid())) {
+        if (!canUserModifyAccounts(Binder.getCallingUid(), account.type)) {
             try {
                 response.onError(AccountManager.ERROR_CODE_UNSUPPORTED_OPERATION,
                         "User cannot modify accounts");
@@ -1512,7 +1513,7 @@
         checkManageAccountsPermission();
 
         // Is user disallowed from modifying accounts?
-        if (!canUserModifyAccounts(Binder.getCallingUid())) {
+        if (!canUserModifyAccounts(Binder.getCallingUid(), accountType)) {
             try {
                 response.onError(AccountManager.ERROR_CODE_USER_RESTRICTED,
                         "User is not allowed to add an account!");
@@ -2758,7 +2759,7 @@
                 Manifest.permission.USE_CREDENTIALS);
     }
 
-    private boolean canUserModifyAccounts(int callingUid) {
+    private boolean canUserModifyAccounts(int callingUid, String accountType) {
         if (callingUid != Process.myUid()) {
             if (getUserManager().getUserRestrictions(
                     new UserHandle(UserHandle.getUserId(callingUid)))
@@ -2766,6 +2767,15 @@
                 return false;
             }
         }
+
+        DevicePolicyManager dpm = (DevicePolicyManager) mContext
+                .getSystemService(Context.DEVICE_POLICY_SERVICE);
+        String[] typesArray = dpm.getAccountTypesWithManagementDisabled();
+        for (String forbiddenType : typesArray) {
+            if (forbiddenType.equals(accountType)) {
+                return false;
+            }
+        }
         return true;
     }
 
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index c67fa66..d6457c3 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -35,6 +35,7 @@
 import android.appwidget.AppWidgetManager;
 import android.graphics.Rect;
 import android.os.BatteryStats;
+import android.os.PersistableBundle;
 import android.service.voice.IVoiceInteractionSession;
 import android.util.ArrayMap;
 
@@ -1341,12 +1342,12 @@
                 String host = "";
                 String port = "";
                 String exclList = "";
-                String pacFileUrl = null;
+                Uri pacFileUrl = Uri.EMPTY;
                 if (proxy != null) {
                     host = proxy.getHost();
                     port = Integer.toString(proxy.getPort());
                     exclList = proxy.getExclusionListAsString();
-                    pacFileUrl = proxy.getPacFileUrl().toString();
+                    pacFileUrl = proxy.getPacFileUrl();
                 }
                 synchronized (ActivityManagerService.this) {
                     for (int i = mLruProcesses.size() - 1 ; i >= 0 ; i--) {
@@ -5441,22 +5442,21 @@
     }
 
     @Override
-    public final void activityPaused(IBinder token) {
+    public final void activityPaused(IBinder token, PersistableBundle persistentState) {
         final long origId = Binder.clearCallingIdentity();
         synchronized(this) {
             ActivityStack stack = ActivityRecord.getStackLocked(token);
             if (stack != null) {
-                stack.activityPausedLocked(token, false);
+                stack.activityPausedLocked(token, false, persistentState);
             }
         }
         Binder.restoreCallingIdentity(origId);
     }
 
     @Override
-    public final void activityStopped(IBinder token, Bundle icicle, Bitmap thumbnail,
-            CharSequence description) {
-        if (localLOGV) Slog.v(
-            TAG, "Activity stopped: token=" + token);
+    public final void activityStopped(IBinder token, Bundle icicle,
+            PersistableBundle persistentState, CharSequence description) {
+        if (localLOGV) Slog.v(TAG, "Activity stopped: token=" + token);
 
         // Refuse possible leaked file descriptors
         if (icicle != null && icicle.hasFileDescriptors()) {
@@ -5468,7 +5468,7 @@
         synchronized (this) {
             ActivityRecord r = ActivityRecord.isInStackLocked(token);
             if (r != null) {
-                r.task.stack.activityStoppedLocked(r, icicle, thumbnail, description);
+                r.task.stack.activityStoppedLocked(r, icicle, persistentState, description);
             }
         }
 
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index efd2b57..8391f79 100755
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -16,6 +16,7 @@
 
 package com.android.server.am;
 
+import android.os.PersistableBundle;
 import android.os.Trace;
 import com.android.internal.app.ResolverActivity;
 import com.android.server.AttributeCache;
@@ -117,6 +118,7 @@
     ProcessRecord app;      // if non-null, hosting application
     ActivityState state;    // current state we are in
     Bundle  icicle;         // last saved activity state
+    PersistableBundle persistentState; // last persistently saved activity state
     boolean frontOfTask;    // is this the root activity of its task?
     boolean launchFailed;   // set if a launched failed, to abort on 2nd try
     boolean haveState;      // have we gotten the last activity state?
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 442da31..2e439ae 100755
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -68,6 +68,7 @@
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
+import android.os.PersistableBundle;
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.os.Trace;
@@ -276,7 +277,7 @@
                         if (r.app != null) {
                             mService.logAppTooSlow(r.app, r.pauseTime, "pausing " + r);
                         }
-                        activityPausedLocked(r.appToken, true);
+                        activityPausedLocked(r.appToken, true, r.persistentState);
                     }
                 } break;
                 case LAUNCH_TICK_MSG: {
@@ -860,13 +861,15 @@
         }
     }
 
-    final void activityPausedLocked(IBinder token, boolean timeout) {
+    final void activityPausedLocked(IBinder token, boolean timeout,
+            PersistableBundle persistentState) {
         if (DEBUG_PAUSE) Slog.v(
             TAG, "Activity paused: token=" + token + ", timeout=" + timeout);
 
         final ActivityRecord r = isInStackLocked(token);
         if (r != null) {
             mHandler.removeMessages(PAUSE_TIMEOUT_MSG, r);
+            r.persistentState = persistentState;
             if (mPausingActivity == r) {
                 if (DEBUG_STATES) Slog.v(TAG, "Moving to PAUSED: " + r
                         + (timeout ? " (due to timeout)" : " (pause complete)"));
@@ -881,13 +884,14 @@
         }
     }
 
-    final void activityStoppedLocked(ActivityRecord r, Bundle icicle, Bitmap thumbnail,
-            CharSequence description) {
+    final void activityStoppedLocked(ActivityRecord r, Bundle icicle,
+            PersistableBundle persistentState, CharSequence description) {
         if (r.state != ActivityState.STOPPING) {
             Slog.i(TAG, "Activity reported stop, but no longer stopping: " + r);
             mHandler.removeMessages(STOP_TIMEOUT_MSG, r);
             return;
         }
+        r.persistentState = persistentState;
         if (DEBUG_SAVED_STATE) Slog.i(TAG, "Saving icicle of " + r + ": " + icicle);
         if (icicle != null) {
             // If icicle is null, this is happening due to a timeout, so we
@@ -895,7 +899,7 @@
             r.icicle = icicle;
             r.haveState = true;
             r.launchCount = 0;
-            r.updateThumbnail(thumbnail, description);
+            r.updateThumbnail(null, description);
         }
         if (!r.stopped) {
             if (DEBUG_STATES) Slog.v(TAG, "Moving to STOPPED: " + r + " (stop complete)");
@@ -1417,7 +1421,8 @@
 
         final TaskRecord nextTask = next.task;
         final TaskRecord prevTask = prev != null ? prev.task : null;
-        if (prevTask != null && prevTask.mOnTopOfHome && prev.finishing && prev.frontOfTask) {
+        if (prevTask != null && prevTask.stack == this &&
+                prevTask.mOnTopOfHome && prev.finishing && prev.frontOfTask) {
             if (DEBUG_STACK)  mStackSupervisor.validateTopActivitiesLocked();
             if (prevTask == nextTask) {
                 prevTask.setFrontOfTask();
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index ce3d853..6f62a03 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -1026,10 +1026,10 @@
             r.clearOptionsLocked();
             app.thread.scheduleLaunchActivity(new Intent(r.intent), r.appToken,
                     System.identityHashCode(r), r.info,
-                    new Configuration(mService.mConfiguration), r.compat,
-                    r.task.voiceInteractor, app.repProcState, r.icicle, results, newIntents,
-                    !andResume, mService.isNextTransitionForward(), profileFile, profileFd,
-                    profileAutoStop, options);
+                    new Configuration(mService.mConfiguration), r.compat, r.task.voiceInteractor,
+                    app.repProcState, r.icicle, r.persistentState, results, newIntents, !andResume,
+                    mService.isNextTransitionForward(), profileFile, profileFd, profileAutoStop,
+                    options);
 
             if ((app.info.flags&ApplicationInfo.FLAG_CANT_SAVE_STATE) != 0) {
                 // This may be a heavy-weight process!  Note that the package
diff --git a/services/core/java/com/android/server/connectivity/PacManager.java b/services/core/java/com/android/server/connectivity/PacManager.java
index 0749f24..63178eb 100644
--- a/services/core/java/com/android/server/connectivity/PacManager.java
+++ b/services/core/java/com/android/server/connectivity/PacManager.java
@@ -25,6 +25,7 @@
 import android.content.IntentFilter;
 import android.content.ServiceConnection;
 import android.net.ProxyInfo;
+import android.net.Uri;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.RemoteException;
@@ -32,7 +33,6 @@
 import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.provider.Settings;
-import android.text.TextUtils;
 import android.util.Log;
 
 import com.android.internal.annotations.GuardedBy;
@@ -71,7 +71,7 @@
     public static final String KEY_PROXY = "keyProxy";
     private String mCurrentPac;
     @GuardedBy("mProxyLock")
-    private String mPacUrl;
+    private Uri mPacUrl;
 
     private AlarmManager mAlarmManager;
     @GuardedBy("mProxyLock")
@@ -100,7 +100,7 @@
         public void run() {
             String file;
             synchronized (mProxyLock) {
-                if (mPacUrl == null) return;
+                if (Uri.EMPTY.equals(mPacUrl)) return;
                 try {
                     file = get(mPacUrl);
                 } catch (IOException ioe) {
@@ -158,13 +158,13 @@
      * @return Returns true when the broadcast should not be sent
      */
     public synchronized boolean setCurrentProxyScriptUrl(ProxyInfo proxy) {
-        if (proxy.getPacFileUrl() != null) {
+        if (!Uri.EMPTY.equals(proxy.getPacFileUrl())) {
             if (proxy.getPacFileUrl().equals(mPacUrl) && (proxy.getPort() > 0)) {
                 // Allow to send broadcast, nothing to do.
                 return false;
             }
             synchronized (mProxyLock) {
-                mPacUrl = proxy.getPacFileUrl().toString();
+                mPacUrl = proxy.getPacFileUrl();
             }
             mCurrentDelay = DELAY_1;
             mHasSentBroadcast = false;
@@ -196,8 +196,8 @@
      *
      * @throws IOException
      */
-    private static String get(String urlString) throws IOException {
-        URL url = new URL(urlString);
+    private static String get(Uri pacUri) throws IOException {
+        URL url = new URL(pacUri.toString());
         URLConnection urlConnection = url.openConnection(java.net.Proxy.NO_PROXY);
         return new String(Streams.readFully(urlConnection.getInputStream()));
     }
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 3ae0fd5..3d5fb57 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -518,6 +518,11 @@
                 // Brighten quickly.
                 slow = false;
             }
+            // If low power mode is enabled, brightness level
+            // would be scaled down to half
+            if (mPowerRequest.lowPowerMode) {
+                target = target/2;
+            }
             animateScreenBrightness(clampScreenBrightness(target),
                     slow ? BRIGHTNESS_RAMP_RATE_SLOW : BRIGHTNESS_RAMP_RATE_FAST);
         } else {
diff --git a/services/core/java/com/android/server/location/FlpHardwareProvider.java b/services/core/java/com/android/server/location/FlpHardwareProvider.java
index 09f1c567..51ee93b 100644
--- a/services/core/java/com/android/server/location/FlpHardwareProvider.java
+++ b/services/core/java/com/android/server/location/FlpHardwareProvider.java
@@ -67,9 +67,9 @@
     public static FlpHardwareProvider getInstance(Context context) {
         if (sSingletonInstance == null) {
             sSingletonInstance = new FlpHardwareProvider(context);
+            sSingletonInstance.nativeInit();
         }
 
-        nativeInit();
         return sSingletonInstance;
     }
 
@@ -96,8 +96,7 @@
                 Looper.myLooper());
     }
 
-    public static boolean isSupported() {
-        nativeInit();
+    public boolean isSupported() {
         return nativeIsSupported();
     }
 
@@ -218,9 +217,9 @@
     // Core members
     private static native void nativeClassInit();
     private static native boolean nativeIsSupported();
-    private static native void nativeInit();
 
     // FlpLocationInterface members
+    private native void nativeInit();
     private native int nativeGetBatchSize();
     private native void nativeStartBatching(int requestId, FusedBatchOptions options);
     private native void nativeUpdateBatchingOptions(int requestId, FusedBatchOptions optionsObject);
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index 015032b..41ab626 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -16,6 +16,7 @@
 
 package com.android.server.media;
 
+import android.app.ActivityManager;
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.media.routeprovider.RouteRequest;
@@ -42,6 +43,7 @@
 import android.os.RemoteException;
 import android.os.ResultReceiver;
 import android.os.SystemClock;
+import android.os.UserHandle;
 import android.text.TextUtils;
 import android.util.Log;
 import android.util.Pair;
@@ -80,7 +82,9 @@
 
     private final MessageHandler mHandler;
 
-    private final int mPid;
+    private final int mOwnerPid;
+    private final int mOwnerUid;
+    private final int mUserId;
     private final SessionInfo mSessionInfo;
     private final String mTag;
     private final ControllerStub mController;
@@ -110,10 +114,12 @@
 
     private boolean mIsActive = false;
 
-    public MediaSessionRecord(int pid, String packageName, ISessionCallback cb, String tag,
-            MediaSessionService service, Handler handler) {
-        mPid = pid;
-        mSessionInfo = new SessionInfo(UUID.randomUUID().toString(), packageName);
+    public MediaSessionRecord(int ownerPid, int ownerUid, int userId, String ownerPackageName,
+            ISessionCallback cb, String tag, MediaSessionService service, Handler handler) {
+        mOwnerPid = ownerPid;
+        mOwnerUid = ownerUid;
+        mUserId = userId;
+        mSessionInfo = new SessionInfo(UUID.randomUUID().toString(), ownerPackageName);
         mTag = tag;
         mController = new ControllerStub();
         mSession = new SessionStub();
@@ -187,6 +193,15 @@
     }
 
     /**
+     * Get the user id this session was created for.
+     *
+     * @return The user id for this session.
+     */
+    public int getUserId() {
+        return mUserId;
+    }
+
+    /**
      * Check if this session has system priorty and should receive media buttons
      * before any other sessions.
      *
@@ -305,7 +320,8 @@
         pw.println(prefix + mTag + " " + this);
 
         final String indent = prefix + "  ";
-        pw.println(indent + "pid=" + mPid);
+        pw.println(indent + "ownerPid=" + mOwnerPid + ", ownerUid=" + mOwnerUid
+                + ", userId=" + mUserId);
         pw.println(indent + "info=" + mSessionInfo.toString());
         pw.println(indent + "published=" + mIsActive);
         pw.println(indent + "flags=" + mFlags);
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index fb858fc..008f9be 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -63,7 +63,6 @@
     private final ArrayList<MediaRouteProviderProxy> mProviders
             = new ArrayList<MediaRouteProviderProxy>();
     private final Object mLock = new Object();
-    // TODO do we want a separate thread for handling mediasession messages?
     private final Handler mHandler = new Handler();
 
     private MediaSessionRecord mPrioritySession;
@@ -72,8 +71,8 @@
     // session so we drop late callbacks properly.
     private int mShowRoutesRequestId = 0;
 
-    // TODO refactor to have per user state. See MediaRouterService for an
-    // example
+    // TODO refactor to have per user state for providers. See
+    // MediaRouterService for an example
 
     public MediaSessionService(Context context) {
         super(context);
@@ -211,25 +210,42 @@
      * <ul>
      * <li>the caller has android.Manifest.permission.MEDIA_CONTENT_CONTROL
      * permission</li>
-     * <li>the caller's listener is one of the enabled notification listeners</li>
+     * <li>the caller's listener is one of the enabled notification listeners
+     * for the caller's user</li>
      * </ul>
      */
-    private void enforceMediaPermissions(ComponentName compName, int pid, int uid) {
+    private void enforceMediaPermissions(ComponentName compName, int pid, int uid,
+            int resolvedUserId) {
         if (getContext()
                 .checkPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL, pid, uid)
                     != PackageManager.PERMISSION_GRANTED
-                && !isEnabledNotificationListener(compName)) {
+                && !isEnabledNotificationListener(compName, UserHandle.getUserId(uid),
+                        resolvedUserId)) {
             throw new SecurityException("Missing permission to control media.");
         }
     }
 
-    private boolean isEnabledNotificationListener(ComponentName compName) {
+    /**
+     * This checks if the component is an enabled notification listener for the
+     * specified user. Enabled components may only operate on behalf of the user
+     * they're running as.
+     *
+     * @param compName The component that is enabled.
+     * @param userId The user id of the caller.
+     * @param forUserId The user id they're making the request on behalf of.
+     * @return True if the component is enabled, false otherwise
+     */
+    private boolean isEnabledNotificationListener(ComponentName compName, int userId,
+            int forUserId) {
+        if (userId != forUserId) {
+            // You may not access another user's content as an enabled listener.
+            return false;
+        }
         if (compName != null) {
-            final int currentUser = ActivityManager.getCurrentUser();
             final String enabledNotifListeners = Settings.Secure.getStringForUser(
                     getContext().getContentResolver(),
                     Settings.Secure.ENABLED_NOTIFICATION_LISTENERS,
-                    currentUser);
+                    userId);
             if (enabledNotifListeners != null) {
                 final String[] components = enabledNotifListeners.split(":");
                 for (int i = 0; i < components.length; i++) {
@@ -248,23 +264,23 @@
             }
             if (DEBUG) {
                 Log.d(TAG, "not ok to get sessions, " + compName +
-                        " is not in list of ENABLED_NOTIFICATION_LISTENERS");
+                        " is not in list of ENABLED_NOTIFICATION_LISTENERS for user " + userId);
             }
         }
         return false;
     }
 
-    private MediaSessionRecord createSessionInternal(int pid, String packageName,
-            ISessionCallback cb, String tag, boolean forCalls) {
+    private MediaSessionRecord createSessionInternal(int callerPid, int callerUid, int userId,
+            String callerPackageName, ISessionCallback cb, String tag) {
         synchronized (mLock) {
-            return createSessionLocked(pid, packageName, cb, tag);
+            return createSessionLocked(callerPid, callerUid, userId, callerPackageName, cb, tag);
         }
     }
 
-    private MediaSessionRecord createSessionLocked(int pid, String packageName,
-            ISessionCallback cb, String tag) {
-        final MediaSessionRecord session = new MediaSessionRecord(pid, packageName, cb, tag, this,
-                mHandler);
+    private MediaSessionRecord createSessionLocked(int callerPid, int callerUid, int userId,
+            String callerPackageName, ISessionCallback cb, String tag) {
+        final MediaSessionRecord session = new MediaSessionRecord(callerPid, callerUid, userId,
+                callerPackageName, cb, tag, this, mHandler);
         try {
             cb.asBinder().linkToDeath(session, 0);
         } catch (RemoteException e) {
@@ -273,7 +289,7 @@
         mRecords.add(session);
         mPriorityStack.addSession(session);
         if (DEBUG) {
-            Log.d(TAG, "Created session for package " + packageName + " with tag " + tag);
+            Log.d(TAG, "Created session for package " + callerPackageName + " with tag " + tag);
         }
         return session;
     }
@@ -358,41 +374,50 @@
         // ActivityManagerNative.handleIncomingUser and stash result for use
         // when starting services on that session's behalf.
         @Override
-        public ISession createSession(String packageName, ISessionCallback cb, String tag)
-                throws RemoteException {
+        public ISession createSession(String packageName, ISessionCallback cb, String tag,
+                int userId) throws RemoteException {
             final int pid = Binder.getCallingPid();
             final int uid = Binder.getCallingUid();
             final long token = Binder.clearCallingIdentity();
             try {
                 enforcePackageName(packageName, uid);
+                int resolvedUserId = ActivityManager.handleIncomingUser(pid, uid, userId,
+                        false /* allowAll */, true /* requireFull */, "createSession", packageName);
                 if (cb == null) {
                     throw new IllegalArgumentException("Controller callback cannot be null");
                 }
-                return createSessionInternal(pid, packageName, cb, tag, false).getSessionBinder();
+                return createSessionInternal(pid, uid, resolvedUserId, packageName, cb, tag)
+                        .getSessionBinder();
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
         }
 
         @Override
-        public List<IBinder> getSessions(ComponentName componentName) {
+        public List<IBinder> getSessions(ComponentName componentName, int userId) {
             final int pid = Binder.getCallingPid();
             final int uid = Binder.getCallingUid();
             final long token = Binder.clearCallingIdentity();
 
             try {
+                String packageName = null;
                 if (componentName != null) {
                     // If they gave us a component name verify they own the
                     // package
-                    enforcePackageName(componentName.getPackageName(), uid);
+                    packageName = componentName.getPackageName();
+                    enforcePackageName(packageName, uid);
                 }
-                // Then check if they have the permissions or their component is
-                // allowed
-                enforceMediaPermissions(componentName, pid, uid);
+                // Check that they can make calls on behalf of the user and
+                // get the final user id
+                int resolvedUserId = ActivityManager.handleIncomingUser(pid, uid, userId,
+                        true /* allowAll */, true /* requireFull */, "getSessions", packageName);
+                // Check if they have the permissions or their component is
+                // enabled for the user they're calling from.
+                enforceMediaPermissions(componentName, pid, uid, resolvedUserId);
                 ArrayList<IBinder> binders = new ArrayList<IBinder>();
                 synchronized (mLock) {
                     ArrayList<MediaSessionRecord> records = mPriorityStack
-                            .getActiveSessions();
+                            .getActiveSessions(resolvedUserId);
                     int size = records.size();
                     for (int i = 0; i < size; i++) {
                         binders.add(records.get(i).getControllerBinder().asBinder());
@@ -428,7 +453,7 @@
                     mRecords.get(i).dump(pw, "");
                     pw.println();
                 }
-                mPriorityStack.dumpLocked(pw, "");
+                mPriorityStack.dump(pw, "");
 
                 pw.println("Providers:");
                 count = mProviders.size();
diff --git a/services/core/java/com/android/server/media/MediaSessionStack.java b/services/core/java/com/android/server/media/MediaSessionStack.java
index f9f004d..1e1818d 100644
--- a/services/core/java/com/android/server/media/MediaSessionStack.java
+++ b/services/core/java/com/android/server/media/MediaSessionStack.java
@@ -18,6 +18,7 @@
 
 import android.media.session.PlaybackState;
 import android.media.session.Session;
+import android.os.UserHandle;
 import android.text.TextUtils;
 
 import java.io.PrintWriter;
@@ -104,11 +105,12 @@
      * Get the current priority sorted list of active sessions. The most
      * important session is at index 0 and the least important at size - 1.
      *
+     * @param userId The user to check.
      * @return All the active sessions in priority order.
      */
-    public ArrayList<MediaSessionRecord> getActiveSessions() {
+    public ArrayList<MediaSessionRecord> getActiveSessions(int userId) {
         if (mCachedActiveList == null) {
-            mCachedActiveList = getPriorityListLocked(true, 0);
+            mCachedActiveList = getPriorityListLocked(true, 0, userId);
         }
         return mCachedActiveList;
     }
@@ -118,13 +120,14 @@
      * transport controls. The most important session is at index 0 and the
      * least important at size -1.
      *
+     * @param userId The user to check.
      * @return All the active sessions that handle transport controls in
      *         priority order.
      */
-    public ArrayList<MediaSessionRecord> getTransportControlSessions() {
+    public ArrayList<MediaSessionRecord> getTransportControlSessions(int userId) {
         if (mCachedTransportControlList == null) {
             mCachedTransportControlList = getPriorityListLocked(true,
-                    Session.FLAG_HANDLES_TRANSPORT_CONTROLS);
+                    Session.FLAG_HANDLES_TRANSPORT_CONTROLS, userId);
         }
         return mCachedTransportControlList;
     }
@@ -132,13 +135,14 @@
     /**
      * Get the highest priority active session.
      *
+     * @param userId The user to check.
      * @return The current highest priority session or null.
      */
-    public MediaSessionRecord getDefaultSession() {
+    public MediaSessionRecord getDefaultSession(int userId) {
         if (mCachedDefault != null) {
             return mCachedDefault;
         }
-        ArrayList<MediaSessionRecord> records = getPriorityListLocked(true, 0);
+        ArrayList<MediaSessionRecord> records = getPriorityListLocked(true, 0, userId);
         if (records.size() > 0) {
             return records.get(0);
         }
@@ -148,22 +152,24 @@
     /**
      * Get the highest priority session that can handle media buttons.
      *
+     * @param userId The user to check.
      * @return The default media button session or null.
      */
-    public MediaSessionRecord getDefaultMediaButtonSession() {
+    public MediaSessionRecord getDefaultMediaButtonSession(int userId) {
         if (mCachedButtonReceiver != null) {
             return mCachedButtonReceiver;
         }
         ArrayList<MediaSessionRecord> records = getPriorityListLocked(true,
-                Session.FLAG_HANDLES_MEDIA_BUTTONS);
+                Session.FLAG_HANDLES_MEDIA_BUTTONS, userId);
         if (records.size() > 0) {
             mCachedButtonReceiver = records.get(0);
         }
         return mCachedButtonReceiver;
     }
 
-    public void dumpLocked(PrintWriter pw, String prefix) {
-        ArrayList<MediaSessionRecord> sortedSessions = getPriorityListLocked(false, 0);
+    public void dump(PrintWriter pw, String prefix) {
+        ArrayList<MediaSessionRecord> sortedSessions = getPriorityListLocked(false, 0,
+                UserHandle.USER_ALL);
         int count = sortedSessions.size();
         pw.println(prefix + "Sessions Stack - have " + count + " sessions:");
         String indent = prefix + "  ";
@@ -182,9 +188,12 @@
      *            all sessions.
      * @param withFlags Only return sessions with all the specified flags set. 0
      *            returns all sessions.
+     * @param userId The user to get sessions for. {@link UserHandle#USER_ALL}
+     *            will return sessions for all users.
      * @return The priority sorted list of sessions.
      */
-    private ArrayList<MediaSessionRecord> getPriorityListLocked(boolean activeOnly, int withFlags) {
+    private ArrayList<MediaSessionRecord> getPriorityListLocked(boolean activeOnly, int withFlags,
+            int userId) {
         ArrayList<MediaSessionRecord> result = new ArrayList<MediaSessionRecord>();
         int lastLocalIndex = 0;
         int lastActiveIndex = 0;
@@ -194,7 +203,12 @@
         for (int i = 0; i < size; i++) {
             final MediaSessionRecord session = mSessions.get(i);
 
+            if (userId != UserHandle.USER_ALL && userId != session.getUserId()) {
+                // Filter out sessions for the wrong user
+                continue;
+            }
             if ((session.getFlags() & withFlags) != withFlags) {
+                // Filter out sessions with the wrong flags
                 continue;
             }
             if (!session.isActive()) {
diff --git a/services/core/java/com/android/server/notification/NotificationComparator.java b/services/core/java/com/android/server/notification/NotificationComparator.java
new file mode 100644
index 0000000..c8b1ba0
--- /dev/null
+++ b/services/core/java/com/android/server/notification/NotificationComparator.java
@@ -0,0 +1,44 @@
+/*
+ * 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.server.notification;
+
+import java.util.Comparator;
+
+/**
+ * Sorts notificaitons into attention-relelvant order.
+ */
+public class NotificationComparator
+        implements Comparator<NotificationManagerService.NotificationRecord> {
+
+    @Override
+    public int compare(NotificationManagerService.NotificationRecord lhs,
+            NotificationManagerService.NotificationRecord rhs) {
+        final int leftScore = lhs.sbn.getScore();
+        final int rightScore = rhs.sbn.getScore();
+        if (leftScore != rightScore) {
+            // by priority, high to low
+            return -1 * Integer.compare(leftScore, rightScore);
+        }
+        final float leftPeple = lhs.getContactAffinity();
+        final float rightPeople = rhs.getContactAffinity();
+        if (leftPeple != rightPeople) {
+            // by contact proximity, close to far
+            return -1 * Float.compare(leftPeple, rightPeople);
+        }
+        // then break ties by time, most recent first
+        return -1 * Long.compare(lhs.sbn.getPostTime(), rhs.sbn.getPostTime());
+    }
+}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index fce86e8..7a4f951 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -49,8 +49,10 @@
 import android.os.Binder;
 import android.os.Environment;
 import android.os.Handler;
+import android.os.HandlerThread;
 import android.os.IBinder;
 import android.os.IInterface;
+import android.os.Looper;
 import android.os.Message;
 import android.os.Process;
 import android.os.RemoteException;
@@ -61,6 +63,7 @@
 import android.service.notification.IConditionListener;
 import android.service.notification.IConditionProvider;
 import android.service.notification.NotificationListenerService;
+import android.service.notification.NotificationOrderUpdate;
 import android.service.notification.StatusBarNotification;
 import android.service.notification.Condition;
 import android.service.notification.ZenModeConfig;
@@ -76,7 +79,6 @@
 import android.widget.Toast;
 
 import com.android.internal.R;
-import com.android.internal.notification.NotificationScorer;
 import com.android.internal.util.FastXmlSerializer;
 import com.android.server.EventLogTags;
 import com.android.server.SystemService;
@@ -104,9 +106,12 @@
 import java.util.ArrayDeque;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.NoSuchElementException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
 
 /** {@hide} */
 public class NotificationManagerService extends SystemService {
@@ -118,6 +123,8 @@
     // message codes
     static final int MESSAGE_TIMEOUT = 2;
     static final int MESSAGE_SAVE_POLICY_FILE = 3;
+    static final int MESSAGE_RECONSIDER_RANKING = 4;
+    static final int MESSAGE_SEND_RANKING_UPDATE = 5;
 
     static final int LONG_DELAY = 3500; // 3.5 seconds
     static final int SHORT_DELAY = 2000; // 2 seconds
@@ -147,6 +154,9 @@
 
     final IBinder mForegroundToken = new Binder();
     private WorkerHandler mHandler;
+    private final HandlerThread mRankingThread = new HandlerThread("ranker",
+            Process.THREAD_PRIORITY_BACKGROUND);
+    private Handler mRankingHandler = null;
 
     private Light mNotificationLight;
     Light mAttentionLight;
@@ -171,6 +181,7 @@
     // used as a mutex for access to all active notifications & listeners
     final ArrayList<NotificationRecord> mNotificationList =
             new ArrayList<NotificationRecord>();
+    final NotificationComparator mRankingComparator = new NotificationComparator();
     final ArrayMap<String, NotificationRecord> mNotificationsByKey =
             new ArrayMap<String, NotificationRecord>();
     final ArrayList<ToastRecord> mToastQueue = new ArrayList<ToastRecord>();
@@ -193,7 +204,7 @@
     private static final String TAG_PACKAGE = "package";
     private static final String ATTR_NAME = "name";
 
-    final ArrayList<NotificationScorer> mScorers = new ArrayList<NotificationScorer>();
+    final ArrayList<NotificationSignalExtractor> mSignalExtractors = new ArrayList<NotificationSignalExtractor>();
 
     private final UserProfiles mUserProfiles = new UserProfiles();
     private NotificationListeners mListeners;
@@ -444,8 +455,9 @@
     public static final class NotificationRecord
     {
         final StatusBarNotification sbn;
-        final SingleNotificationStats stats = new SingleNotificationStats();
+        SingleNotificationStats stats;
         IBinder statusBarKey;
+        private float mContactAffinity;
 
         NotificationRecord(StatusBarNotification sbn)
         {
@@ -528,6 +540,14 @@
                     this.sbn.getTag(), this.sbn.getScore(), this.sbn.getKey(),
                     this.sbn.getNotification());
         }
+
+        public void setContactAffinity(float contactAffinity) {
+            mContactAffinity = contactAffinity;
+        }
+
+        public float getContactAffinity() {
+            return mContactAffinity;
+        }
     }
 
     private static final class ToastRecord
@@ -707,7 +727,7 @@
             boolean queryRemove = false;
             boolean packageChanged = false;
             boolean cancelNotifications = true;
-            
+
             if (action.equals(Intent.ACTION_PACKAGE_ADDED)
                     || (queryRemove=action.equals(Intent.ACTION_PACKAGE_REMOVED))
                     || action.equals(Intent.ACTION_PACKAGE_RESTARTED)
@@ -849,6 +869,8 @@
         mVibrator = (Vibrator) getContext().getSystemService(Context.VIBRATOR_SERVICE);
 
         mHandler = new WorkerHandler();
+        mRankingThread.start();
+        mRankingHandler = new RankingWorkerHandler(mRankingThread.getLooper());
         mZenModeHelper = new ZenModeHelper(getContext(), mHandler);
         mZenModeHelper.addCallback(new ZenModeHelper.Callback() {
             @Override
@@ -925,21 +947,22 @@
 
         mSettingsObserver = new SettingsObserver(mHandler);
 
-        // spin up NotificationScorers
-        String[] notificationScorerNames = resources.getStringArray(
-                R.array.config_notificationScorers);
-        for (String scorerName : notificationScorerNames) {
+        // spin up NotificationSignalExtractors
+        String[] extractorNames = resources.getStringArray(
+                R.array.config_notificationSignalExtractors);
+        for (String extractorName : extractorNames) {
             try {
-                Class<?> scorerClass = getContext().getClassLoader().loadClass(scorerName);
-                NotificationScorer scorer = (NotificationScorer) scorerClass.newInstance();
-                scorer.initialize(getContext());
-                mScorers.add(scorer);
+                Class<?> extractorClass = getContext().getClassLoader().loadClass(extractorName);
+                NotificationSignalExtractor extractor =
+                        (NotificationSignalExtractor) extractorClass.newInstance();
+                extractor.initialize(getContext());
+                mSignalExtractors.add(extractor);
             } catch (ClassNotFoundException e) {
-                Slog.w(TAG, "Couldn't find scorer " + scorerName + ".", e);
+                Slog.w(TAG, "Couldn't find extractor " + extractorName + ".", e);
             } catch (InstantiationException e) {
-                Slog.w(TAG, "Couldn't instantiate scorer " + scorerName + ".", e);
+                Slog.w(TAG, "Couldn't instantiate extractor " + extractorName + ".", e);
             } catch (IllegalAccessException e) {
-                Slog.w(TAG, "Problem accessing scorer " + scorerName + ".", e);
+                Slog.w(TAG, "Problem accessing extractor " + extractorName + ".", e);
             }
         }
 
@@ -1150,6 +1173,7 @@
          * System-only API for getting a list of current (i.e. not cleared) notifications.
          *
          * Requires ACCESS_NOTIFICATIONS which is signature|system.
+         * @returns A list of all the notifications, in natural order.
          */
         @Override
         public StatusBarNotification[] getActiveNotifications(String callingPkg) {
@@ -1306,6 +1330,9 @@
          * should be used.
          *
          * @param token The binder for the listener, to check that the caller is allowed
+         * @param keys the notification keys to fetch, or null for all active notifications.
+         * @returns The return value will contain the notifications specified in keys, in that
+         *      order, or if keys is null, all the notifications, in natural order.
          */
         @Override
         public StatusBarNotification[] getActiveNotificationsFromListener(
@@ -1337,7 +1364,7 @@
 
         @Override
         public String[] getActiveNotificationKeysFromListener(INotificationListener token) {
-            return NotificationManagerService.this.getActiveNotificationKeysFromListener(token);
+            return NotificationManagerService.this.getActiveNotificationKeys(token);
         }
 
         @Override
@@ -1409,19 +1436,21 @@
         }
     };
 
-    private String[] getActiveNotificationKeysFromListener(INotificationListener token) {
-        synchronized (mNotificationList) {
-            final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
-            final ArrayList<String> keys = new ArrayList<String>();
-            final int N = mNotificationList.size();
-            for (int i=0; i<N; i++) {
-                final StatusBarNotification sbn = mNotificationList.get(i).sbn;
-                if (info.enabledAndUserMatches(sbn.getUserId())) {
-                    keys.add(sbn.getKey());
+    private String[] getActiveNotificationKeys(INotificationListener token) {
+        final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
+        final ArrayList<String> keys = new ArrayList<String>();
+        if (info.isEnabledForCurrentProfiles()) {
+            synchronized (mNotificationList) {
+                final int N = mNotificationList.size();
+                for (int i = 0; i < N; i++) {
+                    final StatusBarNotification sbn = mNotificationList.get(i).sbn;
+                    if (info.enabledAndUserMatches(sbn.getUserId())) {
+                        keys.add(sbn.getKey());
+                    }
                 }
             }
-            return keys.toArray(new String[keys.size()]);
         }
+        return keys.toArray(new String[keys.size()]);
     }
 
     void dumpImpl(PrintWriter pw) {
@@ -1578,26 +1607,23 @@
                 // 1. initial score: buckets of 10, around the app
                 int score = notification.priority * NOTIFICATION_PRIORITY_MULTIPLIER; //[-20..20]
 
-                // 2. Consult external heuristics (TBD)
-
-                // 3. Apply local rules
-
-                int initialScore = score;
-                if (!mScorers.isEmpty()) {
-                    if (DBG) Slog.v(TAG, "Initial score is " + score + ".");
-                    for (NotificationScorer scorer : mScorers) {
+                // 2. extract ranking signals from the notification data
+                final StatusBarNotification n = new StatusBarNotification(
+                        pkg, opPkg, id, tag, callingUid, callingPid, score, notification,
+                        user);
+                NotificationRecord r = new NotificationRecord(n);
+                if (!mSignalExtractors.isEmpty()) {
+                    for (NotificationSignalExtractor extractor : mSignalExtractors) {
                         try {
-                            score = scorer.getScore(notification, score);
+                            RankingFuture future = extractor.process(r);
+                            scheduleRankingReconsideration(future);
                         } catch (Throwable t) {
-                            Slog.w(TAG, "Scorer threw on .getScore.", t);
+                            Slog.w(TAG, "NotificationSignalExtractor failed.", t);
                         }
                     }
-                    if (DBG) Slog.v(TAG, "Final score is " + score + ".");
                 }
 
-                // add extra to indicate score modified by NotificationScorer
-                notification.extras.putBoolean(Notification.EXTRA_SCORE_MODIFIED,
-                        score != initialScore);
+                // 3. Apply local rules
 
                 // blocked apps
                 if (ENABLE_BLOCKED_NOTIFICATIONS && !noteNotificationOp(pkg, callingUid)) {
@@ -1608,10 +1634,6 @@
                     }
                 }
 
-                if (DBG) {
-                    Slog.v(TAG, "Assigned score=" + score + " to " + notification);
-                }
-
                 if (score < SCORE_DISPLAY_THRESHOLD) {
                     // Notification will be blocked because the score is too low.
                     return;
@@ -1626,12 +1648,7 @@
                 if (DBG || intercept) Slog.v(TAG,
                         "pkg=" + pkg + " canInterrupt=" + canInterrupt + " intercept=" + intercept);
                 synchronized (mNotificationList) {
-                    final StatusBarNotification n = new StatusBarNotification(
-                            pkg, opPkg, id, tag, callingUid, callingPid, score, notification,
-                            user);
-                    NotificationRecord r = new NotificationRecord(n);
                     NotificationRecord old = null;
-
                     int index = indexOfNotificationLocked(pkg, tag, id, userId);
                     if (index < 0) {
                         mNotificationList.add(r);
@@ -1639,7 +1656,7 @@
                     } else {
                         old = mNotificationList.get(index);
                         mNotificationList.set(index, r);
-                        mUsageStats.registerUpdatedByApp(r);
+                        mUsageStats.registerUpdatedByApp(r, old);
                         // Make sure we don't lose the foreground service state.
                         if (old != null) {
                             notification.flags |=
@@ -1651,6 +1668,8 @@
                     }
                     mNotificationsByKey.put(n.getKey(), r);
 
+                    Collections.sort(mNotificationList, mRankingComparator);
+
                     // Ensure if this is a foreground service that the proper additional
                     // flags are set.
                     if ((notification.flags&Notification.FLAG_FOREGROUND_SERVICE) != 0) {
@@ -1948,6 +1967,57 @@
         }
     }
 
+    private void scheduleRankingReconsideration(RankingFuture future) {
+        if (future != null) {
+            Message m = Message.obtain(mRankingHandler, MESSAGE_RECONSIDER_RANKING, future);
+            long delay = future.getDelay(TimeUnit.MILLISECONDS);
+            mRankingHandler.sendMessageDelayed(m, delay);
+        }
+    }
+
+    private void handleRankingReconsideration(Message message) {
+        if (!(message.obj instanceof RankingFuture)) return;
+
+        RankingFuture future = (RankingFuture) message.obj;
+        future.run();
+        try {
+            NotificationRecord record = future.get();
+            synchronized (mNotificationList) {
+                int before = mNotificationList.indexOf(record);
+                if (before != -1) {
+                    Collections.sort(mNotificationList, mRankingComparator);
+                    int after = mNotificationList.indexOf(record);
+
+                    if (before != after) {
+                        scheduleSendRankingUpdate();
+                    }
+                }
+            }
+        } catch (InterruptedException e) {
+            // we're running the future explicitly, so this should never happen
+        } catch (ExecutionException e) {
+            // we're running the future explicitly, so this should never happen
+        }
+    }
+
+    private void scheduleSendRankingUpdate() {
+        mHandler.removeMessages(MESSAGE_SEND_RANKING_UPDATE);
+        Message m = Message.obtain(mHandler, MESSAGE_SEND_RANKING_UPDATE);
+        mHandler.sendMessage(m);
+    }
+
+    private void handleSendRankingUpdate() {
+        synchronized (mNotificationList) {
+            final int N = mNotificationList.size();
+            ArrayList<StatusBarNotification> sbns =
+                    new ArrayList<StatusBarNotification>(N);
+            for (int i = 0; i < N; i++ ) {
+                sbns.add(mNotificationList.get(i).sbn);
+            }
+            mListeners.notifyOrderUpdateLocked(sbns);
+        }
+    }
+
     private final class WorkerHandler extends Handler
     {
         @Override
@@ -1961,11 +2031,30 @@
                 case MESSAGE_SAVE_POLICY_FILE:
                     handleSavePolicyFile();
                     break;
+                case MESSAGE_SEND_RANKING_UPDATE:
+                    handleSendRankingUpdate();
+                    break;
+            }
+        }
+
+    }
+
+    private final class RankingWorkerHandler extends Handler
+    {
+        public RankingWorkerHandler(Looper looper) {
+            super(looper);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case MESSAGE_RECONSIDER_RANKING:
+                    handleRankingReconsideration(msg);
+                    break;
             }
         }
     }
 
-
     // Notifications
     // ============================================================================
     static int clamp(int x, int low, int high) {
@@ -2346,9 +2435,9 @@
         @Override
         public void onServiceAdded(ManagedServiceInfo info) {
             final INotificationListener listener = (INotificationListener) info.service;
-            final String[] keys = getActiveNotificationKeysFromListener(listener);
+            final String[] keys = getActiveNotificationKeys(listener);
             try {
-                listener.onListenerConnected(keys);
+                listener.onListenerConnected(new NotificationOrderUpdate(keys));
             } catch (RemoteException e) {
                 // we tried
             }
@@ -2361,12 +2450,18 @@
             // make a copy in case changes are made to the underlying Notification object
             final StatusBarNotification sbnClone = sbn.clone();
             for (final ManagedServiceInfo info : mServices) {
-                mHandler.post(new Runnable() {
-                    @Override
-                    public void run() {
-                        notifyPostedIfUserMatch(info, sbnClone);
+                if (info.isEnabledForCurrentProfiles()) {
+                    final INotificationListener listener = (INotificationListener) info.service;
+                    final String[] keys = getActiveNotificationKeys(listener);
+                    if (keys.length > 0) {
+                        mHandler.post(new Runnable() {
+                            @Override
+                            public void run() {
+                                notifyPostedIfUserMatch(info, sbnClone, keys);
+                            }
+                        });
                     }
-                });
+                }
             }
         }
 
@@ -2378,39 +2473,83 @@
             // NOTE: this copy is lightweight: it doesn't include heavyweight parts of the
             // notification
             final StatusBarNotification sbnLight = sbn.cloneLight();
-            for (ManagedServiceInfo serviceInfo : mServices) {
-                final ManagedServiceInfo info = (ManagedServiceInfo) serviceInfo;
+            for (final ManagedServiceInfo info : mServices) {
+                if (info.isEnabledForCurrentProfiles()) {
+                    final INotificationListener listener = (INotificationListener) info.service;
+                    final String[] keys = getActiveNotificationKeys(listener);
+                    mHandler.post(new Runnable() {
+                        @Override
+                        public void run() {
+                            notifyRemovedIfUserMatch(info, sbnLight, keys);
+                        }
+                    });
+                }
+            }
+        }
+
+        /**
+         * asynchronously notify all listeners about a reordering of notifications
+         * @param sbns an array of {@link StatusBarNotification}s to consider.  This code
+         *             must not rely on mutable members of these objects, such as the
+         *             {@link Notification}.
+         */
+        public void notifyOrderUpdateLocked(final ArrayList<StatusBarNotification> sbns) {
+            for (final ManagedServiceInfo serviceInfo : mServices) {
                 mHandler.post(new Runnable() {
                     @Override
                     public void run() {
-                        notifyRemovedIfUserMatch(info, sbnLight);
+                        notifyOrderUpdateIfUserMatch(serviceInfo, sbns);
                     }
                 });
             }
         }
 
-        private void notifyPostedIfUserMatch(ManagedServiceInfo info, StatusBarNotification sbn) {
+        private void notifyPostedIfUserMatch(final ManagedServiceInfo info,
+                final StatusBarNotification sbn, String[] keys) {
             if (!info.enabledAndUserMatches(sbn.getUserId())) {
                 return;
             }
             final INotificationListener listener = (INotificationListener)info.service;
             try {
-                listener.onNotificationPosted(sbn);
+                listener.onNotificationPosted(sbn, new NotificationOrderUpdate(keys));
             } catch (RemoteException ex) {
                 Log.e(TAG, "unable to notify listener (posted): " + listener, ex);
             }
         }
 
-        private void notifyRemovedIfUserMatch(ManagedServiceInfo info, StatusBarNotification sbn) {
+        private void notifyRemovedIfUserMatch(ManagedServiceInfo info, StatusBarNotification sbn,
+                String[] keys) {
             if (!info.enabledAndUserMatches(sbn.getUserId())) {
                 return;
             }
             final INotificationListener listener = (INotificationListener)info.service;
             try {
-                listener.onNotificationRemoved(sbn);
+                listener.onNotificationRemoved(sbn, new NotificationOrderUpdate(keys));
             } catch (RemoteException ex) {
                 Log.e(TAG, "unable to notify listener (removed): " + listener, ex);
             }
         }
+
+        /**
+         * @param sbns an array of {@link StatusBarNotification}s to consider.  This code
+         *             must not rely on mutable members of these objects, such as the
+         *             {@link Notification}.
+         */
+        public void notifyOrderUpdateIfUserMatch(ManagedServiceInfo info,
+                ArrayList<StatusBarNotification> sbns) {
+            ArrayList<String> keys = new ArrayList<String>(sbns.size());
+            for (StatusBarNotification sbn: sbns) {
+                if (info.enabledAndUserMatches(sbn.getUserId())) {
+                    keys.add(sbn.getKey());
+                }
+            }
+            final INotificationListener listener = (INotificationListener)info.service;
+            try {
+                listener.onNotificationOrderUpdate(
+                        new NotificationOrderUpdate(keys.toArray(new String[keys.size()])));
+            } catch (RemoteException ex) {
+                Log.e(TAG, "unable to notify listener (ranking update): " + listener, ex);
+            }
+        }
     }
 }
diff --git a/services/core/java/com/android/server/notification/NotificationSignalExtractor.java b/services/core/java/com/android/server/notification/NotificationSignalExtractor.java
new file mode 100644
index 0000000..a41fdfe
--- /dev/null
+++ b/services/core/java/com/android/server/notification/NotificationSignalExtractor.java
@@ -0,0 +1,41 @@
+/*
+* 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.server.notification;
+
+import android.content.Context;
+
+/**
+ * Extracts signals that will be useful to the {@link NotificationComparator} and caches them
+ *  on the {@link NotificationManagerService.NotificationRecord} object. These annotations will
+ *  not be passed on to {@link android.service.notification.NotificationListenerService}s.
+ */
+public interface NotificationSignalExtractor {
+
+    /** One-time initialization. */
+    public void initialize(Context context);
+
+    /**
+     * Called once per notification that is posted or updated.
+     *
+     * @return null if the work is done, or a future if there is more to do. The
+     * {@link RankingFuture} will be run on a worker thread, and if notifications are re-ordered
+     * by that execution, the {@link NotificationManagerService} may send order update
+     * events to the {@link android.service.notification.NotificationListenerService}s.
+     */
+    public RankingFuture process(NotificationManagerService.NotificationRecord notification);
+
+}
diff --git a/services/core/java/com/android/server/notification/NotificationUsageStats.java b/services/core/java/com/android/server/notification/NotificationUsageStats.java
index 45ab3d3..a60e95b 100644
--- a/services/core/java/com/android/server/notification/NotificationUsageStats.java
+++ b/services/core/java/com/android/server/notification/NotificationUsageStats.java
@@ -58,6 +58,7 @@
      * Called when a notification has been posted.
      */
     public synchronized void registerPostedByApp(NotificationRecord notification) {
+        notification.stats = new SingleNotificationStats();
         notification.stats.posttimeElapsedMs = SystemClock.elapsedRealtime();
         for (AggregatedStats stats : getAggregatedStatsLocked(notification)) {
             stats.numPostedByApp++;
@@ -68,7 +69,8 @@
     /**
      * Called when a notification has been updated.
      */
-    public void registerUpdatedByApp(NotificationRecord notification) {
+    public void registerUpdatedByApp(NotificationRecord notification, NotificationRecord old) {
+        notification.stats = old.stats;
         for (AggregatedStats stats : getAggregatedStatsLocked(notification)) {
             stats.numUpdatedByApp++;
         }
diff --git a/services/core/java/com/android/server/notification/RankingFuture.java b/services/core/java/com/android/server/notification/RankingFuture.java
new file mode 100644
index 0000000..33aad8d
--- /dev/null
+++ b/services/core/java/com/android/server/notification/RankingFuture.java
@@ -0,0 +1,118 @@
+/*
+ * 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.server.notification;
+
+import java.util.concurrent.Delayed;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+public abstract class RankingFuture
+        implements ScheduledFuture<NotificationManagerService.NotificationRecord> {
+    private static final long IMMEDIATE = 0l;
+
+    private static final int START = 0;
+    private static final int RUNNING = 1;
+    private static final int DONE = 2;
+    private static final int CANCELLED = 3;
+
+    private int mState;
+    private long mDelay;
+    protected NotificationManagerService.NotificationRecord mRecord;
+
+    public RankingFuture(NotificationManagerService.NotificationRecord record) {
+        this(record, IMMEDIATE);
+    }
+
+    public RankingFuture(NotificationManagerService.NotificationRecord record, long delay) {
+        mDelay = delay;
+        mRecord = record;
+        mState = START;
+    }
+
+    public void run() {
+        if (mState == START) {
+            mState = RUNNING;
+
+            work();
+
+            mState = DONE;
+            synchronized (this) {
+                notifyAll();
+            }
+        }
+    }
+
+    @Override
+    public long getDelay(TimeUnit unit) {
+        return unit.convert(mDelay, TimeUnit.MILLISECONDS);
+    }
+
+    @Override
+    public int compareTo(Delayed another) {
+        return Long.compare(getDelay(TimeUnit.MICROSECONDS),
+                another.getDelay(TimeUnit.MICROSECONDS));
+    }
+
+    @Override
+    public boolean cancel(boolean mayInterruptIfRunning) {
+        if (mState == START) {  // can't cancel if running or done
+            mState = CANCELLED;
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    public boolean isCancelled() {
+        return mState == CANCELLED;
+    }
+
+    @Override
+    public boolean isDone() {
+        return mState == DONE;
+    }
+
+    @Override
+    public NotificationManagerService.NotificationRecord get()
+            throws InterruptedException, ExecutionException {
+        while (!isDone()) {
+            synchronized (this) {
+                this.wait();
+            }
+        }
+        return mRecord;
+    }
+
+    @Override
+    public NotificationManagerService.NotificationRecord get(long timeout, TimeUnit unit)
+            throws InterruptedException, ExecutionException, TimeoutException {
+        long timeoutMillis = unit.convert(timeout, TimeUnit.MILLISECONDS);
+        long start = System.currentTimeMillis();
+        long now = System.currentTimeMillis();
+        while (!isDone() && (now - start) < timeoutMillis) {
+            try {
+                wait(timeoutMillis - (now - start));
+            } catch (InterruptedException e) {
+                now = System.currentTimeMillis();
+            }
+        }
+        return mRecord;
+    }
+
+    public abstract void work();
+}
diff --git a/services/core/java/com/android/server/notification/ValidateNotificationPeople.java b/services/core/java/com/android/server/notification/ValidateNotificationPeople.java
new file mode 100644
index 0000000..8cd2f9b2
--- /dev/null
+++ b/services/core/java/com/android/server/notification/ValidateNotificationPeople.java
@@ -0,0 +1,298 @@
+/*
+* 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.server.notification;
+
+import android.app.Notification;
+import android.content.Context;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.Contacts;
+import android.provider.Settings;
+import android.text.TextUtils;
+import android.util.LruCache;
+import android.util.Slog;
+
+import com.android.server.notification.NotificationManagerService.NotificationRecord;
+
+import java.util.ArrayList;
+import java.util.LinkedList;
+
+/**
+ * This {@link NotificationSignalExtractor} attempts to validate
+ * people references. Also elevates the priority of real people.
+ */
+public class ValidateNotificationPeople implements NotificationSignalExtractor {
+    private static final String TAG = "ValidateNotificationPeople";
+    private static final boolean INFO = true;
+    private static final boolean DEBUG = false;
+
+    private static final boolean ENABLE_PEOPLE_VALIDATOR = true;
+    private static final String SETTING_ENABLE_PEOPLE_VALIDATOR =
+            "validate_notification_people_enabled";
+    private static final String[] LOOKUP_PROJECTION = { Contacts._ID };
+    private static final int MAX_PEOPLE = 10;
+    private static final int PEOPLE_CACHE_SIZE = 200;
+
+    private static final float NONE = 0f;
+    private static final float VALID_CONTACT = 0.5f;
+    // TODO private static final float STARRED_CONTACT = 1f;
+
+    protected boolean mEnabled;
+    private Context mContext;
+
+    // maps raw person handle to resolved person object
+    private LruCache<String, LookupResult> mPeopleCache;
+
+    private RankingFuture validatePeople(NotificationRecord record) {
+        float affinity = NONE;
+        Bundle extras = record.getNotification().extras;
+        if (extras == null) {
+            return null;
+        }
+
+        final String[] people = getExtraPeople(extras);
+        if (people == null || people.length == 0) {
+            return null;
+        }
+
+        if (INFO) Slog.i(TAG, "Validating: " + record.sbn.getKey());
+        final LinkedList<String> pendingLookups = new LinkedList<String>();
+        for (int personIdx = 0; personIdx < people.length && personIdx < MAX_PEOPLE; personIdx++) {
+            final String handle = people[personIdx];
+            if (TextUtils.isEmpty(handle)) continue;
+
+            synchronized (mPeopleCache) {
+                LookupResult lookupResult = mPeopleCache.get(handle);
+                if (lookupResult == null || lookupResult.isExpired()) {
+                    pendingLookups.add(handle);
+                } else {
+                    if (DEBUG) Slog.d(TAG, "using cached lookupResult: " + lookupResult.mId);
+                }
+                if (lookupResult != null) {
+                    affinity = Math.max(affinity, lookupResult.getAffinity());
+                }
+            }
+        }
+
+        // record the best available data, so far:
+        record.setContactAffinity(affinity);
+
+        if (pendingLookups.isEmpty()) {
+            if (INFO) Slog.i(TAG, "final affinity: " + affinity);
+            return null;
+        }
+
+        if (DEBUG) Slog.d(TAG, "Pending: future work scheduled for: " + record.sbn.getKey());
+        return new RankingFuture(record) {
+            @Override
+            public void work() {
+                if (INFO) Slog.i(TAG, "Executing: validation for: " + mRecord.sbn.getKey());
+                float affinity = NONE;
+                LookupResult lookupResult = null;
+                for (final String handle: pendingLookups) {
+                    final Uri uri = Uri.parse(handle);
+                    if ("tel".equals(uri.getScheme())) {
+                        if (DEBUG) Slog.d(TAG, "checking telephone URI: " + handle);
+                        lookupResult = resolvePhoneContact(handle, uri.getSchemeSpecificPart());
+                    } else if (handle.startsWith(Contacts.CONTENT_LOOKUP_URI.toString())) {
+                        if (DEBUG) Slog.d(TAG, "checking lookup URI: " + handle);
+                        lookupResult = resolveContactsUri(handle, uri);
+                    } else {
+                        Slog.w(TAG, "unsupported URI " + handle);
+                    }
+                }
+                if (lookupResult != null) {
+                    affinity = Math.max(affinity, lookupResult.getAffinity());
+                }
+
+                float affinityBound = mRecord.getContactAffinity();
+                affinity = Math.max(affinity, affinityBound);
+                mRecord.setContactAffinity(affinity);
+                if (INFO) Slog.i(TAG, "final affinity: " + affinity);
+            }
+        };
+    }
+
+    private String[] getExtraPeople(Bundle extras) {
+        String[] people = extras.getStringArray(Notification.EXTRA_PEOPLE);
+        if (people != null) {
+            return people;
+        }
+
+        ArrayList<String> stringArray = extras.getStringArrayList(Notification.EXTRA_PEOPLE);
+        if (stringArray != null) {
+            return (String[]) stringArray.toArray();
+        }
+
+        String string = extras.getString(Notification.EXTRA_PEOPLE);
+        if (string != null) {
+            people = new String[1];
+            people[0] = string;
+            return people;
+        }
+        char[] charArray = extras.getCharArray(Notification.EXTRA_PEOPLE);
+        if (charArray != null) {
+            people = new String[1];
+            people[0] = new String(charArray);
+            return people;
+        }
+
+        CharSequence charSeq = extras.getCharSequence(Notification.EXTRA_PEOPLE);
+        if (charSeq != null) {
+            people = new String[1];
+            people[0] = charSeq.toString();
+            return people;
+        }
+
+        CharSequence[] charSeqArray = extras.getCharSequenceArray(Notification.EXTRA_PEOPLE);
+        if (charSeqArray != null) {
+            final int N = charSeqArray.length;
+            people = new String[N];
+            for (int i = 0; i < N; i++) {
+                people[i] = charSeqArray[i].toString();
+            }
+            return people;
+        }
+
+        ArrayList<CharSequence> charSeqList =
+                extras.getCharSequenceArrayList(Notification.EXTRA_PEOPLE);
+        if (charSeqList != null) {
+            final int N = charSeqList.size();
+            people = new String[N];
+            for (int i = 0; i < N; i++) {
+                people[i] = charSeqList.get(i).toString();
+            }
+            return people;
+        }
+        return null;
+    }
+
+    private LookupResult resolvePhoneContact(final String handle, final String number) {
+        LookupResult lookupResult = null;
+        Cursor c = null;
+        try {
+            Uri numberUri = Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI,
+                    Uri.encode(number));
+            c = mContext.getContentResolver().query(numberUri, LOOKUP_PROJECTION, null, null, null);
+            if (c != null && c.getCount() > 0) {
+                c.moveToFirst();
+                final int idIdx = c.getColumnIndex(Contacts._ID);
+                final int id = c.getInt(idIdx);
+                if (DEBUG) Slog.d(TAG, "is valid: " + id);
+                lookupResult = new LookupResult(id);
+            }
+        } catch(Throwable t) {
+            Slog.w(TAG, "Problem getting content resolver or performing contacts query.", t);
+        } finally {
+            if (c != null) {
+                c.close();
+            }
+        }
+        if (lookupResult == null) {
+            lookupResult = new LookupResult(LookupResult.INVALID_ID);
+        }
+        synchronized (mPeopleCache) {
+            mPeopleCache.put(handle, lookupResult);
+        }
+        return lookupResult;
+    }
+
+    private LookupResult resolveContactsUri(String handle, final Uri personUri) {
+        LookupResult lookupResult = null;
+        Cursor c = null;
+        try {
+            c = mContext.getContentResolver().query(personUri, LOOKUP_PROJECTION, null, null, null);
+            if (c != null && c.getCount() > 0) {
+                c.moveToFirst();
+                final int idIdx = c.getColumnIndex(Contacts._ID);
+                final int id = c.getInt(idIdx);
+                if (DEBUG) Slog.d(TAG, "is valid: " + id);
+                lookupResult = new LookupResult(id);
+            }
+        } catch(Throwable t) {
+            Slog.w(TAG, "Problem getting content resolver or performing contacts query.", t);
+        } finally {
+            if (c != null) {
+                c.close();
+            }
+        }
+        if (lookupResult == null) {
+            lookupResult = new LookupResult(LookupResult.INVALID_ID);
+        }
+        synchronized (mPeopleCache) {
+            mPeopleCache.put(handle, lookupResult);
+        }
+        return lookupResult;
+    }
+
+    public void initialize(Context context) {
+        if (DEBUG) Slog.d(TAG, "Initializing  " + getClass().getSimpleName() + ".");
+        mContext = context;
+        mPeopleCache = new LruCache<String, LookupResult>(PEOPLE_CACHE_SIZE);
+        mEnabled = ENABLE_PEOPLE_VALIDATOR && 1 == Settings.Global.getInt(
+                mContext.getContentResolver(), SETTING_ENABLE_PEOPLE_VALIDATOR, 1);
+    }
+
+    public RankingFuture process(NotificationManagerService.NotificationRecord record) {
+        if (!mEnabled) {
+            if (INFO) Slog.i(TAG, "disabled");
+            return null;
+        }
+        if (record == null || record.getNotification() == null) {
+            if (INFO) Slog.i(TAG, "skipping empty notification");
+            return null;
+        }
+        return validatePeople(record);
+    }
+
+    private static class LookupResult {
+        private static final long CONTACT_REFRESH_MILLIS = 60 * 60 * 1000;  // 1hr
+        public static final int INVALID_ID = -1;
+
+        private final long mExpireMillis;
+        private int mId;
+
+        public LookupResult(int id) {
+            mId = id;
+            mExpireMillis = System.currentTimeMillis() + CONTACT_REFRESH_MILLIS;
+        }
+
+        public boolean isExpired() {
+            return mExpireMillis < System.currentTimeMillis();
+        }
+
+        public boolean isInvalid() {
+            return mId == INVALID_ID || isExpired();
+        }
+
+        public float getAffinity() {
+            if (isInvalid()) {
+                return NONE;
+            } else {
+                return VALID_CONTACT;  // TODO: finer grained result: stars
+            }
+        }
+
+        public LookupResult setId(int id) {
+            mId = id;
+            return this;
+        }
+    }
+}
+
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index c8b61f1..d1333b2 100755
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -613,9 +613,15 @@
         private final AtomicLong mLastWritten = new AtomicLong(0);
         private final AtomicBoolean mBackgroundWriteRunning = new AtomicBoolean(false);
 
+        private boolean mIsFirstBoot = false;
+
+        boolean isFirstBoot() {
+            return mIsFirstBoot;
+        }
+
         void write(boolean force) {
             if (force) {
-                write();
+                writeInternal();
                 return;
             }
             if (SystemClock.elapsedRealtime() - mLastWritten.get() < WRITE_INTERVAL
@@ -627,7 +633,7 @@
                     @Override
                     public void run() {
                         try {
-                            write(true);
+                            writeInternal();
                         } finally {
                             mBackgroundWriteRunning.set(false);
                         }
@@ -636,7 +642,7 @@
             }
         }
 
-        private void write() {
+        private void writeInternal() {
             synchronized (mPackages) {
                 synchronized (mFileLock) {
                     AtomicFile file = getFile();
@@ -701,6 +707,7 @@
                         pkg.mLastPackageUsageTimeInMills = timeInMillis;
                     }
                 } catch (FileNotFoundException expected) {
+                    mIsFirstBoot = true;
                 } catch (IOException e) {
                     Log.w(TAG, "Failed to read package usage times", e);
                 } finally {
@@ -1745,7 +1752,7 @@
 
     @Override
     public boolean isFirstBoot() {
-        return !mRestoredSettings;
+        return !mRestoredSettings || mPackageUsage.isFirstBoot();
     }
 
     @Override
@@ -4547,7 +4554,7 @@
             if (updateUsage) {
                 p.mLastPackageUsageTimeInMills = System.currentTimeMillis();
             }
-            mPackageUsage.write();
+            mPackageUsage.write(false);
             if (!p.mDexOptNeeded) {
                 return false;
             }
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 6d2e8592..a5eccb3 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -168,9 +168,9 @@
     // Poll interval in milliseconds for watching boot animation finished.
     private static final int BOOT_ANIMATION_POLL_INTERVAL = 200;
 
-    //powerHint
+    // Used to send the hint to the PowerHAL indicating transitions
+    // from and to the low power mode.
     private static final int POWER_HINT_LOW_POWER_MODE = 5;
-    private static boolean mLowPowerModeEnabled;
 
     private final Context mContext;
     private LightsManager mLightsManager;
@@ -399,6 +399,9 @@
     // Time when we last logged a warning about calling userActivity() without permission.
     private long mLastWarningAboutUserActivityPermission = Long.MIN_VALUE;
 
+    // If true, the device is in low power mode.
+    private static boolean mLowPowerModeEnabled;
+
     private native void nativeInit();
 
     private static native void nativeAcquireSuspendBlocker(String name);
@@ -617,12 +620,11 @@
                 Settings.System.SCREEN_BRIGHTNESS_MODE,
                 Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL, UserHandle.USER_CURRENT);
 
-        boolean mIsEnabled = Settings.Global.getInt(resolver,
-            Settings.Global.LOW_POWER_MODE, 0) != 0;
-        if (mIsEnabled != mLowPowerModeEnabled) {
-            BinderService bs = new BinderService();
-            bs.powerHint(POWER_HINT_LOW_POWER_MODE, mIsEnabled ? 1 : 0);
-            mLowPowerModeEnabled = mIsEnabled;
+        boolean lowPowerModeEnabled = Settings.Global.getInt(resolver,
+                Settings.Global.LOW_POWER_MODE, 0) != 0;
+        if (lowPowerModeEnabled != mLowPowerModeEnabled) {
+            powerHintInternal(POWER_HINT_LOW_POWER_MODE, lowPowerModeEnabled ? 1 : 0);
+            mLowPowerModeEnabled = lowPowerModeEnabled;
         }
 
         mDirty |= DIRTY_SETTINGS;
@@ -1634,6 +1636,8 @@
 
             mDisplayPowerRequest.blockScreenOn = mScreenOnBlocker.isHeld();
 
+            mDisplayPowerRequest.lowPowerMode = mLowPowerModeEnabled;
+
             mDisplayReady = mDisplayManagerInternal.requestPowerState(mDisplayPowerRequest,
                     mRequestWaitForNegativeProximity);
             mRequestWaitForNegativeProximity = false;
@@ -2018,6 +2022,10 @@
         }
     }
 
+    private void powerHintInternal(int hintId, int data) {
+        nativeSendPowerHint(hintId, data);
+    }
+
     /**
      * Low-level function turn the device off immediately, without trying
      * to be clean.  Most people should use {@link ShutdownThread} for a clean shutdown.
@@ -2527,7 +2535,7 @@
         @Override // Binder call
         public void powerHint(int hintId, int data) {
             mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null);
-            nativeSendPowerHint(hintId, data);
+            powerHintInternal(hintId, data);
         }
 
         @Override // Binder call
diff --git a/services/core/java/com/android/server/task/StateChangedListener.java b/services/core/java/com/android/server/task/StateChangedListener.java
new file mode 100644
index 0000000..a87bf95
--- /dev/null
+++ b/services/core/java/com/android/server/task/StateChangedListener.java
@@ -0,0 +1,40 @@
+/*
+ * 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.server.task;
+
+import com.android.server.task.controllers.TaskStatus;
+
+/**
+ * Interface through which a {@link StateController} informs the
+ * {@link com.android.server.task.TaskManagerService} that there are some tasks potentially ready
+ * to be run.
+ */
+public interface StateChangedListener {
+    /**
+     * Called by the controller to notify the TaskManager that it should check on the state of a
+     * task.
+     * @param taskStatus The state of the task which has changed.
+     */
+    public void onTaskStateChanged(TaskStatus taskStatus);
+
+    /**
+     * Called by the controller to notify the TaskManager that regardless of the state of the task,
+     * it must be run immediately.
+     * @param taskStatus The state of the task which is to be run immediately.
+     */
+    public void onTaskDeadlineExpired(TaskStatus taskStatus);
+}
diff --git a/services/core/java/com/android/server/task/TaskList.java b/services/core/java/com/android/server/task/TaskList.java
new file mode 100644
index 0000000..d2b8440
--- /dev/null
+++ b/services/core/java/com/android/server/task/TaskList.java
@@ -0,0 +1,78 @@
+/*
+ * 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.server.task;
+
+import android.content.ComponentName;
+import android.content.Task;
+
+import com.android.server.task.controllers.TaskStatus;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Maintain a list of classes, and accessor methods/logic for these tasks.
+ * This class offers the following functionality:
+ *     - When a task is added, it will determine if the task requirements have changed (update) and
+ *       whether the controllers need to be updated.
+ *     - Persists Tasks, figures out when to to rewrite the Task to disk.
+ *     - Is threadsafe.
+ *     - Handles rescheduling of tasks.
+ *       - When a periodic task is executed and must be re-added.
+ *       - When a task fails and the client requests that it be retried with backoff.
+ */
+public class TaskList {
+
+    final List<TaskStatus> mTasks;
+
+    TaskList() {
+        mTasks = intialiseTaskMapFromDisk();
+    }
+
+    /**
+     * Add a task to the master list, persisting it if necessary.
+     * @param task Task to add.
+     * @param persistable true if the TaskQueue should persist this task to the disk.
+     * @return true if this operation was successful. If false, this task was neither added nor
+     * persisted.
+     */
+    // TODO: implement this when i decide whether i want to key by TaskStatus
+    public boolean add(Task task, boolean persistable) {
+        return true;
+    }
+
+    /**
+     * Remove the provided task. Will also delete the task if it was persisted. Note that this
+     * function does not return the validity of the operation, as we assume a delete will always
+     * succeed.
+     * @param task Task to remove.
+     */
+    public void remove(Task task) {
+
+    }
+
+    /**
+     *
+     * @return
+     */
+    // TODO: Implement this.
+    private List<TaskStatus> intialiseTaskMapFromDisk() {
+        return new ArrayList<TaskStatus>();
+    }
+}
diff --git a/services/core/java/com/android/server/task/TaskManagerService.java b/services/core/java/com/android/server/task/TaskManagerService.java
new file mode 100644
index 0000000..5df4b2a
--- /dev/null
+++ b/services/core/java/com/android/server/task/TaskManagerService.java
@@ -0,0 +1,128 @@
+/*
+ * 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.server.task;
+
+import android.content.Context;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.util.SparseArray;
+
+import com.android.server.task.controllers.TaskStatus;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Responsible for taking tasks representing work to be performed by a client app, and determining
+ * based on the criteria specified when that task should be run against the client application's
+ * endpoint.
+ * @hide
+ */
+public class TaskManagerService extends com.android.server.SystemService
+        implements StateChangedListener {
+
+    /** Master list of tasks. */
+    private final TaskList mTaskList;
+
+    /**
+     * Track Services that have currently active or pending tasks. The index is provided by
+     * {@link TaskStatus#getServiceToken()}
+     */
+    private final SparseArray<TaskServiceContext> mPendingTaskServices =
+            new SparseArray<TaskServiceContext>();
+
+    private final TaskHandler mHandler;
+
+    private class TaskHandler extends Handler {
+        /** 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;
+
+        public TaskHandler(Looper looper) {
+            super(looper);
+        }
+
+        @Override
+        public void handleMessage(Message message) {
+            switch (message.what) {
+                case MSG_RUN_PENDING:
+
+                    break;
+                case MSG_STOP_TASK:
+
+                    break;
+            }
+        }
+
+        /**
+         * Helper to post a message to this handler that will run through the pending queue and
+         * start any tasks it can.
+         */
+        void sendRunPendingTasksMessage() {
+            Message m = Message.obtain(this, MSG_RUN_PENDING);
+            m.sendToTarget();
+        }
+
+        void sendOnStopMessage(TaskStatus taskStatus) {
+
+        }
+    }
+
+    /**
+     * Initializes the system service.
+     * <p>
+     * Subclasses must define a single argument constructor that accepts the context
+     * and passes it to super.
+     * </p>
+     *
+     * @param context The system server context.
+     */
+    public TaskManagerService(Context context) {
+        super(context);
+        mTaskList = new TaskList();
+        mHandler = new TaskHandler(context.getMainLooper());
+    }
+
+    @Override
+    public void onStart() {
+
+    }
+
+    /**
+     * Offboard work to our handler thread as quickly as possible, b/c this call is probably being
+     * made on the main thread.
+     * @param taskStatus The state of the task which has changed.
+     */
+    @Override
+    public void onTaskStateChanged(TaskStatus taskStatus) {
+        if (taskStatus.isReady()) {
+
+        } else {
+            if (mPendingTaskServices.get(taskStatus.getServiceToken()) != null) {
+                // The task is either pending or being executed, which we have to cancel.
+            }
+        }
+
+    }
+
+    @Override
+    public void onTaskDeadlineExpired(TaskStatus taskStatus) {
+
+    }
+}
diff --git a/services/core/java/com/android/server/task/TaskServiceContext.java b/services/core/java/com/android/server/task/TaskServiceContext.java
new file mode 100644
index 0000000..65c6fa5
--- /dev/null
+++ b/services/core/java/com/android/server/task/TaskServiceContext.java
@@ -0,0 +1,94 @@
+/*
+ * 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.server.task;
+
+import android.app.task.ITaskCallback;
+import android.app.task.ITaskService;
+import android.content.ComponentName;
+import android.content.ServiceConnection;
+import android.content.Task;
+import android.os.IBinder;
+
+import com.android.server.task.controllers.TaskStatus;
+
+/**
+ * Maintains information required to bind to a {@link android.app.task.TaskService}. This binding
+ * can then be reused to start concurrent tasks on the TaskService. Information here is unique
+ * within this service.
+ * Functionality provided by this class:
+ *     - Managages wakelock for the service.
+ *     - Sends onStartTask() and onStopTask() messages to client app, and handles callbacks.
+ *     -
+ */
+public class TaskServiceContext extends ITaskCallback.Stub implements ServiceConnection {
+
+    final ComponentName component;
+    int uid;
+    ITaskService service;
+
+    /** Whether this service is actively bound. */
+    boolean mBound;
+
+    TaskServiceContext(Task task) {
+        this.component = task.getService();
+    }
+
+    public void stopTask() {
+
+    }
+
+    public void startTask(Task task) {
+
+    }
+
+    @Override
+    public void taskFinished(int taskId, boolean reschedule) {
+
+    }
+
+    @Override
+    public void acknowledgeStopMessage(int taskId) {
+
+    }
+
+    @Override
+    public void acknowledgeStartMessage(int taskId) {
+
+    }
+
+    /**
+     * @return true if this task is pending or active within this context.
+     */
+    public boolean hasTaskPending(TaskStatus taskStatus) {
+        return true;
+    }
+
+    public boolean isBound() {
+        return mBound;
+    }
+
+    @Override
+    public void onServiceConnected(ComponentName name, IBinder service) {
+
+        mBound = true;
+    }
+
+    @Override
+    public void onServiceDisconnected(ComponentName name) {
+        mBound = false;
+    }
+}
diff --git a/services/core/java/com/android/server/task/controllers/ConnectivityController.java b/services/core/java/com/android/server/task/controllers/ConnectivityController.java
new file mode 100644
index 0000000..5cca77c
--- /dev/null
+++ b/services/core/java/com/android/server/task/controllers/ConnectivityController.java
@@ -0,0 +1,119 @@
+/*
+ * 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.server.task.controllers;
+
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.os.UserHandle;
+import android.util.Log;
+
+import com.android.server.task.TaskManagerService;
+
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ *
+ */
+public class ConnectivityController extends StateController {
+    private static final String TAG = "TaskManager.Connectivity";
+
+    private final List<TaskStatus> mTrackedTasks = new LinkedList<TaskStatus>();
+    private final BroadcastReceiver mConnectivityChangedReceiver =
+            new ConnectivityChangedReceiver();
+
+    public ConnectivityController(TaskManagerService service) {
+        super(service);
+        // Register connectivity changed BR.
+        IntentFilter intentFilter = new IntentFilter();
+        intentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
+        mContext.registerReceiverAsUser(
+                mConnectivityChangedReceiver, UserHandle.ALL, intentFilter, null, null);
+    }
+
+    @Override
+    public void maybeTrackTaskState(TaskStatus taskStatus) {
+        if (taskStatus.hasConnectivityConstraint() || taskStatus.hasMeteredConstraint()) {
+            mTrackedTasks.add(taskStatus);
+        }
+    }
+
+    @Override
+    public void removeTaskStateIfTracked(TaskStatus taskStatus) {
+        mTrackedTasks.remove(taskStatus);
+    }
+
+    /**
+     * @param isConnected Whether the active network is connected for the given uid
+     * @param isMetered Whether the active network is metered for the given uid. This is
+     *                  necessarily false if <code>isConnected</code> is false.
+     * @param userId Id of the user for whom we are updating the connectivity state.
+     */
+    private void updateTrackedTasks(boolean isConnected, boolean isMetered, int userId) {
+        for (TaskStatus ts : mTrackedTasks) {
+            if (ts.userId != userId) {
+                continue;
+            }
+            boolean prevIsConnected = ts.connectivityConstraintSatisfied.getAndSet(isConnected);
+            boolean prevIsMetered = ts.meteredConstraintSatisfied.getAndSet(isMetered);
+            if (prevIsConnected != isConnected || prevIsMetered != isMetered) {
+                    mStateChangedListener.onTaskStateChanged(ts);
+            }
+        }
+    }
+
+    class ConnectivityChangedReceiver extends BroadcastReceiver {
+        /**
+         * We'll receive connectivity changes for each user here, which we'll process independently.
+         * We are only interested in the active network here. We're only interested in the active
+         * network, b/c the end result of this will be for apps to try to hit the network.
+         * @param context The Context in which the receiver is running.
+         * @param intent The Intent being received.
+         */
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            final String action = intent.getAction();
+            if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
+                final int networkType =
+                        intent.getIntExtra(ConnectivityManager.EXTRA_NETWORK_TYPE,
+                                ConnectivityManager.TYPE_NONE);
+                // Connectivity manager for THIS context - important!
+                final ConnectivityManager connManager = (ConnectivityManager)
+                        context.getSystemService(Context.CONNECTIVITY_SERVICE);
+                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();
+                    boolean isMetered = false;
+                    boolean isConnected =
+                            !intent.getBooleanExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, false);
+                    if (isConnected) {  // No point making the call if we know there's no conn.
+                        isMetered = connManager.isActiveNetworkMetered();
+                    }
+                    updateTrackedTasks(isConnected, isMetered, userid);
+                }
+            } else {
+                Log.w(TAG, "Unrecognised action in intent: " + action);
+            }
+        }
+    };
+}
diff --git a/services/core/java/com/android/server/task/controllers/StateController.java b/services/core/java/com/android/server/task/controllers/StateController.java
new file mode 100644
index 0000000..e1cd662
--- /dev/null
+++ b/services/core/java/com/android/server/task/controllers/StateController.java
@@ -0,0 +1,51 @@
+/*
+ * 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.server.task.controllers;
+
+import android.content.Context;
+
+import com.android.server.task.StateChangedListener;
+import com.android.server.task.TaskManagerService;
+
+/**
+ * Incorporates shared controller logic between the various controllers of the TaskManager.
+ * These are solely responsible for tracking a list of tasks, and notifying the TM when these
+ * are ready to run, or whether they must be stopped.
+ */
+public abstract class StateController {
+
+    protected Context mContext;
+    protected StateChangedListener mStateChangedListener;
+
+    public StateController(TaskManagerService service) {
+        mStateChangedListener = service;
+        mContext = service.getContext();
+    }
+
+    /**
+     * Implement the logic here to decide whether a task should be tracked by this controller.
+     * This logic is put here so the TaskManger can be completely agnostic of Controller logic.
+     * Also called when updating a task, so implementing controllers have to be aware of
+     * preexisting tasks.
+     */
+    public abstract void maybeTrackTaskState(TaskStatus taskStatus);
+    /**
+     * Remove task - this will happen if the task is cancelled, completed, etc.
+     */
+    public abstract void removeTaskStateIfTracked(TaskStatus taskStatus);
+
+}
diff --git a/services/core/java/com/android/server/task/controllers/TaskStatus.java b/services/core/java/com/android/server/task/controllers/TaskStatus.java
new file mode 100644
index 0000000..230b049
--- /dev/null
+++ b/services/core/java/com/android/server/task/controllers/TaskStatus.java
@@ -0,0 +1,154 @@
+/*
+ * 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.server.task.controllers;
+
+import android.content.ComponentName;
+import android.content.Task;
+import android.os.SystemClock;
+
+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.
+ * 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.
+ * This isn't strictly necessary because each controller is only interested in a specific field,
+ * and the receivers that are listening for global state change will all run on the main looper,
+ * but we don't enforce that so this is safer.
+ * @hide
+ */
+public class TaskStatus {
+    final int taskId;
+    final int userId;
+    ComponentName component;
+
+    final AtomicBoolean chargingConstraintSatisfied = new AtomicBoolean();
+    final AtomicBoolean timeConstraintSatisfied = new AtomicBoolean();
+    final AtomicBoolean idleConstraintSatisfied = new AtomicBoolean();
+    final AtomicBoolean meteredConstraintSatisfied = new AtomicBoolean();
+    final AtomicBoolean connectivityConstraintSatisfied = new AtomicBoolean();
+
+    private final boolean hasChargingConstraint;
+    private final boolean hasTimingConstraint;
+    private final boolean hasIdleConstraint;
+    private final boolean hasMeteredConstraint;
+    private final boolean hasConnectivityConstraint;
+
+    private long earliestRunTimeElapsedMillis;
+    private long latestRunTimeElapsedMillis;
+
+    /** Provide a unique handle to the service that this task will be run on. */
+    public int getServiceToken() {
+        return component.hashCode() + userId;
+    }
+
+    /** Generate a TaskStatus object for a given task and uid. */
+    // TODO: reimplement this to reuse these objects instead of creating a new one each time?
+    static TaskStatus getForTaskAndUid(Task task, int uId) {
+        return new TaskStatus(task, uId);
+    }
+
+    /** Set up the state of a newly scheduled task. */
+    TaskStatus(Task task, int userId) {
+        this.taskId = task.getTaskId();
+        this.userId = userId;
+        this.component = task.getService();
+
+        hasChargingConstraint = task.isRequireCharging();
+        hasIdleConstraint = task.isRequireDeviceIdle();
+
+        // Timing constraints
+        if (task.isPeriodic()) {
+            long elapsedNow = SystemClock.elapsedRealtime();
+            earliestRunTimeElapsedMillis = elapsedNow;
+            latestRunTimeElapsedMillis = elapsedNow + task.getIntervalMillis();
+            hasTimingConstraint = true;
+        } else if (task.getMinLatencyMillis() != 0L || task.getMaxExecutionDelayMillis() != 0L) {
+            earliestRunTimeElapsedMillis = task.getMinLatencyMillis() > 0L ?
+                    task.getMinLatencyMillis() : Long.MAX_VALUE;
+            latestRunTimeElapsedMillis = task.getMaxExecutionDelayMillis() > 0L ?
+                    task.getMaxExecutionDelayMillis() : Long.MAX_VALUE;
+            hasTimingConstraint = true;
+        } else {
+            hasTimingConstraint = false;
+        }
+
+        // Networking constraints
+        hasMeteredConstraint = task.getNetworkCapabilities() == Task.NetworkType.UNMETERED;
+        hasConnectivityConstraint = task.getNetworkCapabilities() == Task.NetworkType.ANY;
+    }
+
+    boolean hasConnectivityConstraint() {
+        return hasConnectivityConstraint;
+    }
+
+    boolean hasMeteredConstraint() {
+        return hasMeteredConstraint;
+    }
+
+    boolean hasChargingConstraint() {
+        return hasChargingConstraint;
+    }
+
+    boolean hasTimingConstraint() {
+        return hasTimingConstraint;
+    }
+
+    boolean hasIdleConstraint() {
+        return hasIdleConstraint;
+    }
+
+    long getEarliestRunTime() {
+        return earliestRunTimeElapsedMillis;
+    }
+
+    long getLatestRunTime() {
+        return latestRunTimeElapsedMillis;
+    }
+
+    /**
+     * @return whether this task is ready to run, based on its requirements.
+     */
+    public synchronized boolean isReady() {
+        return (!hasChargingConstraint || chargingConstraintSatisfied.get())
+                && (!hasTimingConstraint || timeConstraintSatisfied.get())
+                && (!hasConnectivityConstraint || connectivityConstraintSatisfied.get())
+                && (!hasMeteredConstraint || meteredConstraintSatisfied.get())
+                && (!hasIdleConstraint || idleConstraintSatisfied.get());
+    }
+
+    @Override
+    public int hashCode() {
+        int result = component.hashCode();
+        result = 31 * result + taskId;
+        result = 31 * result + userId;
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof TaskStatus)) return false;
+
+        TaskStatus that = (TaskStatus) o;
+        return ((taskId == that.taskId)
+                && (userId == that.userId)
+                && (component.equals(that.component)));
+    }
+}
diff --git a/services/core/java/com/android/server/task/controllers/TimeController.java b/services/core/java/com/android/server/task/controllers/TimeController.java
new file mode 100644
index 0000000..6d97a53
--- /dev/null
+++ b/services/core/java/com/android/server/task/controllers/TimeController.java
@@ -0,0 +1,230 @@
+/*
+ * 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.server.task.controllers;
+
+import android.app.AlarmManager;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.SystemClock;
+import android.util.Log;
+
+import com.android.server.task.TaskManagerService;
+
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.ListIterator;
+
+/**
+ * This class sets an alarm for the next expiring task, and determines whether a task's minimum
+ * delay has been satisfied.
+ */
+public class TimeController extends StateController {
+    private static final String TAG = "TaskManager.Time";
+    private static final String ACTION_TASK_EXPIRED =
+            "android.content.taskmanager.TASK_EXPIRED";
+    private static final String ACTION_TASK_DELAY_EXPIRED =
+            "android.content.taskmanager.TASK_DELAY_EXPIRED";
+
+    /** Set an alarm for the next task expiry. */
+    private final PendingIntent mTaskExpiredAlarmIntent;
+    /** Set an alarm for the next task delay expiry. This*/
+    private final PendingIntent mNextDelayExpiredAlarmIntent;
+
+    private long mNextTaskExpiredElapsedMillis;
+    private long mNextDelayExpiredElapsedMillis;
+
+    private AlarmManager mAlarmService = null;
+    /** List of tracked tasks, sorted asc. by deadline */
+    private final List<TaskStatus> mTrackedTasks = new LinkedList<TaskStatus>();
+
+    public TimeController(TaskManagerService service) {
+        super(service);
+        mTaskExpiredAlarmIntent =
+                PendingIntent.getBroadcast(mContext, 0 /* ignored */,
+                        new Intent(ACTION_TASK_EXPIRED), 0);
+        mNextDelayExpiredAlarmIntent =
+                PendingIntent.getBroadcast(mContext, 0 /* ignored */,
+                        new Intent(ACTION_TASK_DELAY_EXPIRED), 0);
+
+        // Register BR for these intents.
+        IntentFilter intentFilter = new IntentFilter(ACTION_TASK_EXPIRED);
+        intentFilter.addAction(ACTION_TASK_DELAY_EXPIRED);
+        mContext.registerReceiver(mAlarmExpiredReceiver, intentFilter);
+    }
+
+    /**
+     * Check if the task has a timing constraint, and if so determine where to insert it in our
+     * list.
+     */
+    @Override
+    public synchronized void maybeTrackTaskState(TaskStatus task) {
+        if (task.hasTimingConstraint()) {
+            ListIterator<TaskStatus> it = mTrackedTasks.listIterator(mTrackedTasks.size());
+            while (it.hasPrevious()) {
+                TaskStatus ts = it.previous();
+                if (ts.equals(task)) {
+                    // Update
+                    it.remove();
+                    it.add(task);
+                    break;
+                } else if (ts.getLatestRunTime() < task.getLatestRunTime()) {
+                    // Insert
+                    it.add(task);
+                    break;
+                }
+            }
+            maybeUpdateAlarms(task.getEarliestRunTime(), task.getLatestRunTime());
+        }
+    }
+
+    /**
+     * If the task passed in is being tracked, figure out if we need to update our alarms, and if
+     * so, update them.
+     */
+    @Override
+    public synchronized void removeTaskStateIfTracked(TaskStatus taskStatus) {
+        if (mTrackedTasks.remove(taskStatus)) {
+            if (mNextDelayExpiredElapsedMillis <= taskStatus.getEarliestRunTime()) {
+                handleTaskDelayExpired();
+            }
+            if (mNextTaskExpiredElapsedMillis <= taskStatus.getLatestRunTime()) {
+                handleTaskDeadlineExpired();
+            }
+        }
+    }
+
+    /**
+     * Set an alarm with the {@link android.app.AlarmManager} for the next time at which a task's
+     * delay will expire.
+     * This alarm <b>will not</b> wake up the phone.
+     */
+    private void setDelayExpiredAlarm(long alarmTimeElapsedMillis) {
+        ensureAlarmService();
+        mAlarmService.set(AlarmManager.ELAPSED_REALTIME, alarmTimeElapsedMillis,
+                mNextDelayExpiredAlarmIntent);
+    }
+
+    /**
+     * Set an alarm with the {@link android.app.AlarmManager} for the next time at which a task's
+     * deadline will expire.
+     * This alarm <b>will</b> wake up the phone.
+     */
+    private void setDeadlineExpiredAlarm(long alarmTimeElapsedMillis) {
+        ensureAlarmService();
+        mAlarmService.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, alarmTimeElapsedMillis,
+                mTaskExpiredAlarmIntent);
+    }
+
+    /**
+     * Determines whether this controller can stop tracking the given task.
+     * The controller is no longer interested in a task once its time constraint is satisfied, and
+     * the task's deadline is fulfilled - unlike other controllers a time constraint can't toggle
+     * back and forth.
+     */
+    private boolean canStopTrackingTask(TaskStatus taskStatus) {
+        final long elapsedNowMillis = SystemClock.elapsedRealtime();
+        return taskStatus.timeConstraintSatisfied.get() &&
+                (taskStatus.getLatestRunTime() == Long.MAX_VALUE ||
+                        taskStatus.getLatestRunTime() < elapsedNowMillis);
+    }
+
+    private void maybeUpdateAlarms(long delayExpiredElapsed, long deadlineExpiredElapsed) {
+        if (delayExpiredElapsed < mNextDelayExpiredElapsedMillis) {
+            mNextDelayExpiredElapsedMillis = delayExpiredElapsed;
+            setDelayExpiredAlarm(mNextDelayExpiredElapsedMillis);
+        }
+        if (deadlineExpiredElapsed < mNextTaskExpiredElapsedMillis) {
+            mNextTaskExpiredElapsedMillis = deadlineExpiredElapsed;
+            setDeadlineExpiredAlarm(mNextTaskExpiredElapsedMillis);
+        }
+    }
+
+    private void ensureAlarmService() {
+        if (mAlarmService == null) {
+            mAlarmService = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
+        }
+    }
+
+    /**
+     * Handles alarm that notifies that a task has expired. When this function is called at least
+     * one task must be run.
+     */
+    private synchronized void handleTaskDeadlineExpired() {
+        long nextExpiryTime = Long.MAX_VALUE;
+        final long nowElapsedMillis = SystemClock.elapsedRealtime();
+
+        Iterator<TaskStatus> it = mTrackedTasks.iterator();
+        while (it.hasNext()) {
+            TaskStatus ts = it.next();
+            final long taskDeadline = ts.getLatestRunTime();
+
+            if (taskDeadline <= nowElapsedMillis) {
+                ts.timeConstraintSatisfied.set(true);
+                mStateChangedListener.onTaskDeadlineExpired(ts);
+                it.remove();
+            } else {  // Sorted by expiry time, so take the next one and stop.
+                nextExpiryTime = taskDeadline;
+                break;
+            }
+        }
+        maybeUpdateAlarms(Long.MAX_VALUE, nextExpiryTime);
+    }
+
+    /**
+     * Handles alarm that notifies us that a task's delay has expired. Iterates through the list of
+     * tracked tasks and marks them as ready as appropriate.
+     */
+    private synchronized void handleTaskDelayExpired() {
+        final long nowElapsedMillis = SystemClock.elapsedRealtime();
+        long nextDelayTime = Long.MAX_VALUE;
+
+        Iterator<TaskStatus> it = mTrackedTasks.iterator();
+        while (it.hasNext()) {
+            final TaskStatus ts = it.next();
+            final long taskDelayTime = ts.getEarliestRunTime();
+            if (taskDelayTime < nowElapsedMillis) {
+                ts.timeConstraintSatisfied.set(true);
+                mStateChangedListener.onTaskStateChanged(ts);
+                if (canStopTrackingTask(ts)) {
+                    it.remove();
+                }
+            } else {  // Keep going through list to get next delay time.
+                if (nextDelayTime > taskDelayTime) {
+                    nextDelayTime = taskDelayTime;
+                }
+            }
+        }
+        maybeUpdateAlarms(nextDelayTime, Long.MAX_VALUE);
+    }
+
+    private final BroadcastReceiver mAlarmExpiredReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            // An task has just expired, so we run through the list of tasks that we have and
+            // notify our StateChangedListener.
+            if (ACTION_TASK_EXPIRED.equals(intent.getAction())) {
+                handleTaskDeadlineExpired();
+            } else if (ACTION_TASK_DELAY_EXPIRED.equals(intent.getAction())) {
+                handleTaskDelayExpired();
+            }
+        }
+    };
+}
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index 50dd27d..05f9947 100644
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -134,7 +134,7 @@
 
     private void buildTvInputListLocked(int userId) {
         UserState userState = getUserStateLocked(userId);
-        userState.inputList.clear();
+        userState.inputMap.clear();
 
         if (DEBUG) Slog.d(TAG, "buildTvInputList");
         PackageManager pm = mContext.getPackageManager();
@@ -149,7 +149,7 @@
             }
             TvInputInfo info = new TvInputInfo(ri);
             if (DEBUG) Slog.d(TAG, "add " + info.getId());
-            userState.inputList.add(info);
+            userState.inputMap.put(info.getId(), info);
         }
     }
 
@@ -179,9 +179,9 @@
             }
             // Release created sessions.
             for (SessionState state : userState.sessionStateMap.values()) {
-                if (state.session != null) {
+                if (state.mSession != null) {
                     try {
-                        state.session.release();
+                        state.mSession.release();
                     } catch (RemoteException e) {
                         Slog.e(TAG, "error in release", e);
                     }
@@ -191,15 +191,15 @@
 
             // Unregister all callbacks and unbind all services.
             for (ServiceState serviceState : userState.serviceStateMap.values()) {
-                if (serviceState.callback != null) {
+                if (serviceState.mCallback != null) {
                     try {
-                        serviceState.service.unregisterCallback(serviceState.callback);
+                        serviceState.mService.unregisterCallback(serviceState.mCallback);
                     } catch (RemoteException e) {
                         Slog.e(TAG, "error in unregisterCallback", e);
                     }
                 }
-                serviceState.clients.clear();
-                mContext.unbindService(serviceState.connection);
+                serviceState.mClients.clear();
+                mContext.unbindService(serviceState.mConnection);
             }
             userState.serviceStateMap.clear();
 
@@ -215,28 +215,33 @@
         return userState;
     }
 
-    private ServiceState getServiceStateLocked(ComponentName name, int userId) {
+    private ServiceState getServiceStateLocked(String inputId, int userId) {
         UserState userState = getUserStateLocked(userId);
-        ServiceState serviceState = userState.serviceStateMap.get(name);
+        ServiceState serviceState = userState.serviceStateMap.get(inputId);
         if (serviceState == null) {
-            throw new IllegalStateException("Service state not found for " + name + " (userId="
+            throw new IllegalStateException("Service state not found for " + inputId + " (userId="
                     + userId + ")");
         }
         return serviceState;
     }
 
-    private ITvInputSession getSessionLocked(IBinder sessionToken, int callingUid, int userId) {
+    private SessionState getSessionStateLocked(IBinder sessionToken, int callingUid, int userId) {
         UserState userState = getUserStateLocked(userId);
         SessionState sessionState = userState.sessionStateMap.get(sessionToken);
         if (sessionState == null) {
             throw new IllegalArgumentException("Session state not found for token " + sessionToken);
         }
         // Only the application that requested this session or the system can access it.
-        if (callingUid != Process.SYSTEM_UID && callingUid != sessionState.callingUid) {
+        if (callingUid != Process.SYSTEM_UID && callingUid != sessionState.mCallingUid) {
             throw new SecurityException("Illegal access to the session with token " + sessionToken
                     + " from uid " + callingUid);
         }
-        ITvInputSession session = sessionState.session;
+        return sessionState;
+    }
+
+    private ITvInputSession getSessionLocked(IBinder sessionToken, int callingUid, int userId) {
+        SessionState sessionState = getSessionStateLocked(sessionToken, callingUid, userId);
+        ITvInputSession session = sessionState.mSession;
         if (session == null) {
             throw new IllegalStateException("Session not yet created for token " + sessionToken);
         }
@@ -249,38 +254,47 @@
                 false, methodName, null);
     }
 
-    private void updateServiceConnectionLocked(ComponentName name, int userId) {
+    private void updateServiceConnectionLocked(String inputId, int userId) {
         UserState userState = getUserStateLocked(userId);
-        ServiceState serviceState = userState.serviceStateMap.get(name);
+        ServiceState serviceState = userState.serviceStateMap.get(inputId);
         if (serviceState == null) {
             return;
         }
-        boolean isStateEmpty = serviceState.clients.isEmpty()
-                && serviceState.sessionTokens.isEmpty();
-        if (serviceState.service == null && !isStateEmpty && userId == mCurrentUserId) {
+        if (serviceState.mReconnecting) {
+            if (!serviceState.mSessionTokens.isEmpty()) {
+                // wait until all the sessions are removed.
+                return;
+            }
+            serviceState.mReconnecting = false;
+        }
+        boolean isStateEmpty = serviceState.mClients.isEmpty()
+                && serviceState.mSessionTokens.isEmpty();
+        if (serviceState.mService == null && !isStateEmpty && userId == mCurrentUserId) {
             // This means that the service is not yet connected but its state indicates that we
             // have pending requests. Then, connect the service.
-            if (serviceState.bound) {
+            if (serviceState.mBound) {
                 // We have already bound to the service so we don't try to bind again until after we
                 // unbind later on.
                 return;
             }
             if (DEBUG) {
-                Slog.d(TAG, "bindServiceAsUser(name=" + name.getClassName() + ", userId=" + userId
+                Slog.d(TAG, "bindServiceAsUser(inputId=" + inputId + ", userId=" + userId
                         + ")");
             }
-            Intent i = new Intent(TvInputService.SERVICE_INTERFACE).setComponent(name);
-            mContext.bindServiceAsUser(i, serviceState.connection, Context.BIND_AUTO_CREATE,
+
+            Intent i = new Intent(TvInputService.SERVICE_INTERFACE).setComponent(
+                    userState.inputMap.get(inputId).getComponent());
+            mContext.bindServiceAsUser(i, serviceState.mConnection, Context.BIND_AUTO_CREATE,
                     new UserHandle(userId));
-            serviceState.bound = true;
-        } else if (serviceState.service != null && isStateEmpty) {
+            serviceState.mBound = true;
+        } else if (serviceState.mService != null && isStateEmpty) {
             // This means that the service is already connected but its state indicates that we have
             // nothing to do with it. Then, disconnect the service.
             if (DEBUG) {
-                Slog.d(TAG, "unbindService(name=" + name.getClassName() + ")");
+                Slog.d(TAG, "unbindService(inputId=" + inputId + ")");
             }
-            mContext.unbindService(serviceState.connection);
-            userState.serviceStateMap.remove(name);
+            mContext.unbindService(serviceState.mConnection);
+            userState.serviceStateMap.remove(inputId);
         }
     }
 
@@ -289,8 +303,7 @@
         final SessionState sessionState =
                 getUserStateLocked(userId).sessionStateMap.get(sessionToken);
         if (DEBUG) {
-            Slog.d(TAG, "createSessionInternalLocked(name=" + sessionState.name.getClassName()
-                    + ")");
+            Slog.d(TAG, "createSessionInternalLocked(inputId=" + sessionState.mInputId + ")");
         }
 
         final InputChannel[] channels = InputChannel.openInputChannelPair(sessionToken.toString());
@@ -300,17 +313,22 @@
             @Override
             public void onSessionCreated(ITvInputSession session) {
                 if (DEBUG) {
-                    Slog.d(TAG, "onSessionCreated(name=" + sessionState.name.getClassName() + ")");
+                    Slog.d(TAG, "onSessionCreated(inputId=" + sessionState.mInputId + ")");
                 }
                 synchronized (mLock) {
-                    sessionState.session = session;
+                    sessionState.mSession = session;
                     if (session == null) {
                         removeSessionStateLocked(sessionToken, userId);
-                        sendSessionTokenToClientLocked(sessionState.client, sessionState.name, null,
-                                null, sessionState.seq, userId);
+                        sendSessionTokenToClientLocked(sessionState.mClient, sessionState.mInputId,
+                                null, null, sessionState.mSeq, userId);
                     } else {
-                        sendSessionTokenToClientLocked(sessionState.client, sessionState.name,
-                                sessionToken, channels[0], sessionState.seq, userId);
+                        try {
+                            session.asBinder().linkToDeath(sessionState, 0);
+                        } catch (RemoteException e) {
+                            Slog.e(TAG, "Session is already died.");
+                        }
+                        sendSessionTokenToClientLocked(sessionState.mClient, sessionState.mInputId,
+                                sessionToken, channels[0], sessionState.mSeq, userId);
                     }
                     channels[0].dispose();
                 }
@@ -323,24 +341,32 @@
         } catch (RemoteException e) {
             Slog.e(TAG, "error in createSession", e);
             removeSessionStateLocked(sessionToken, userId);
-            sendSessionTokenToClientLocked(sessionState.client, sessionState.name, null, null,
-                    sessionState.seq, userId);
+            sendSessionTokenToClientLocked(sessionState.mClient, sessionState.mInputId, null, null,
+                    sessionState.mSeq, userId);
         }
         channels[1].dispose();
     }
 
-    private void sendSessionTokenToClientLocked(ITvInputClient client, ComponentName name,
+    private void sendSessionTokenToClientLocked(ITvInputClient client, String inputId,
             IBinder sessionToken, InputChannel channel, int seq, int userId) {
         try {
-            client.onSessionCreated(name, sessionToken, channel, seq);
+            client.onSessionCreated(inputId, sessionToken, channel, seq);
         } catch (RemoteException exception) {
             Slog.e(TAG, "error in onSessionCreated", exception);
         }
+    }
 
-        if (sessionToken == null) {
-            // This means that the session creation failed. We might want to disconnect the service.
-            updateServiceConnectionLocked(name, userId);
+    private void releaseSessionLocked(IBinder sessionToken, int callingUid, int userId) {
+        SessionState sessionState = getSessionStateLocked(sessionToken, callingUid, userId);
+        if (sessionState.mSession != null) {
+            try {
+                sessionState.mSession.release();
+            } catch (RemoteException e) {
+                Slog.w(TAG, "session is already disapeared", e);
+            }
+            sessionState.mSession = null;
         }
+        removeSessionStateLocked(sessionToken, userId);
     }
 
     private void removeSessionStateLocked(IBinder sessionToken, int userId) {
@@ -349,19 +375,31 @@
         SessionState sessionState = userState.sessionStateMap.remove(sessionToken);
 
         // Close the open log entry, if any.
-        if (sessionState.logUri != null) {
+        if (sessionState.mLogUri != null) {
             SomeArgs args = SomeArgs.obtain();
-            args.arg1 = sessionState.logUri;
+            args.arg1 = sessionState.mLogUri;
             args.arg2 = System.currentTimeMillis();
             mLogHandler.obtainMessage(LogHandler.MSG_CLOSE_ENTRY, args).sendToTarget();
         }
 
         // Also remove the session token from the session token list of the current service.
-        ServiceState serviceState = userState.serviceStateMap.get(sessionState.name);
+        ServiceState serviceState = userState.serviceStateMap.get(sessionState.mInputId);
         if (serviceState != null) {
-            serviceState.sessionTokens.remove(sessionToken);
+            serviceState.mSessionTokens.remove(sessionToken);
         }
-        updateServiceConnectionLocked(sessionState.name, userId);
+        updateServiceConnectionLocked(sessionState.mInputId, userId);
+    }
+
+    private void broadcastServiceAvailabilityChangedLocked(ServiceState serviceState) {
+        for (IBinder iBinder : serviceState.mClients) {
+            ITvInputClient client = ITvInputClient.Stub.asInterface(iBinder);
+            try {
+                client.onAvailabilityChanged(
+                        serviceState.mTvInputInfo.getId(), serviceState.mAvailable);
+            } catch (RemoteException e) {
+                Slog.e(TAG, "error in onAvailabilityChanged", e);
+            }
+        }
     }
 
     private final class BinderService extends ITvInputManager.Stub {
@@ -373,7 +411,7 @@
             try {
                 synchronized (mLock) {
                     UserState userState = getUserStateLocked(resolvedUserId);
-                    return new ArrayList<TvInputInfo>(userState.inputList);
+                    return new ArrayList<TvInputInfo>(userState.inputMap.values());
                 }
             } finally {
                 Binder.restoreCallingIdentity(identity);
@@ -381,7 +419,7 @@
         }
 
         @Override
-        public boolean getAvailability(final ITvInputClient client, final ComponentName name,
+        public boolean getAvailability(final ITvInputClient client, final String inputId,
                 int userId) {
             final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
                     Binder.getCallingUid(), userId, "getAvailability");
@@ -389,11 +427,11 @@
             try {
                 synchronized (mLock) {
                     UserState userState = getUserStateLocked(resolvedUserId);
-                    ServiceState serviceState = userState.serviceStateMap.get(name);
+                    ServiceState serviceState = userState.serviceStateMap.get(inputId);
                     if (serviceState != null) {
                         // We already know the status of this input service. Return the cached
                         // status.
-                        return serviceState.available;
+                        return serviceState.mAvailable;
                     }
                 }
             } finally {
@@ -403,7 +441,7 @@
         }
 
         @Override
-        public void registerCallback(final ITvInputClient client, final ComponentName name,
+        public void registerCallback(final ITvInputClient client, final String inputId,
                 int userId) {
             final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
                     Binder.getCallingUid(), userId, "registerCallback");
@@ -413,28 +451,29 @@
                     // Create a new service callback and add it to the callback map of the current
                     // service.
                     UserState userState = getUserStateLocked(resolvedUserId);
-                    ServiceState serviceState = userState.serviceStateMap.get(name);
+                    ServiceState serviceState = userState.serviceStateMap.get(inputId);
                     if (serviceState == null) {
-                        serviceState = new ServiceState(resolvedUserId);
-                        userState.serviceStateMap.put(name, serviceState);
+                        serviceState = new ServiceState(
+                                userState.inputMap.get(inputId), resolvedUserId);
+                        userState.serviceStateMap.put(inputId, serviceState);
                     }
                     IBinder iBinder = client.asBinder();
-                    if (!serviceState.clients.contains(iBinder)) {
-                        serviceState.clients.add(iBinder);
+                    if (!serviceState.mClients.contains(iBinder)) {
+                        serviceState.mClients.add(iBinder);
                     }
-                    if (serviceState.service != null) {
-                        if (serviceState.callback != null) {
+                    if (serviceState.mService != null) {
+                        if (serviceState.mCallback != null) {
                             // We already handled.
                             return;
                         }
-                        serviceState.callback = new ServiceCallback(resolvedUserId);
+                        serviceState.mCallback = new ServiceCallback(resolvedUserId);
                         try {
-                            serviceState.service.registerCallback(serviceState.callback);
+                            serviceState.mService.registerCallback(serviceState.mCallback);
                         } catch (RemoteException e) {
                             Slog.e(TAG, "error in registerCallback", e);
                         }
                     } else {
-                        updateServiceConnectionLocked(name, resolvedUserId);
+                        updateServiceConnectionLocked(inputId, resolvedUserId);
                     }
                 }
             } finally {
@@ -443,34 +482,34 @@
         }
 
         @Override
-        public void unregisterCallback(ITvInputClient client, ComponentName name, int userId) {
+        public void unregisterCallback(ITvInputClient client, String inputId, int userId) {
             final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(),
                     Binder.getCallingUid(), userId, "unregisterCallback");
             final long identity = Binder.clearCallingIdentity();
             try {
                 synchronized (mLock) {
                     UserState userState = getUserStateLocked(resolvedUserId);
-                    ServiceState serviceState = userState.serviceStateMap.get(name);
+                    ServiceState serviceState = userState.serviceStateMap.get(inputId);
                     if (serviceState == null) {
                         return;
                     }
 
                     // Remove this client from the client list and unregister the callback.
-                    serviceState.clients.remove(client.asBinder());
-                    if (!serviceState.clients.isEmpty()) {
+                    serviceState.mClients.remove(client.asBinder());
+                    if (!serviceState.mClients.isEmpty()) {
                         // We have other clients who want to keep the callback. Do this later.
                         return;
                     }
-                    if (serviceState.service == null || serviceState.callback == null) {
+                    if (serviceState.mService == null || serviceState.mCallback == null) {
                         return;
                     }
                     try {
-                        serviceState.service.unregisterCallback(serviceState.callback);
+                        serviceState.mService.unregisterCallback(serviceState.mCallback);
                     } catch (RemoteException e) {
                         Slog.e(TAG, "error in unregisterCallback", e);
                     } finally {
-                        serviceState.callback = null;
-                        updateServiceConnectionLocked(name, resolvedUserId);
+                        serviceState.mCallback = null;
+                        updateServiceConnectionLocked(inputId, resolvedUserId);
                     }
                 }
             } finally {
@@ -479,7 +518,7 @@
         }
 
         @Override
-        public void createSession(final ITvInputClient client, final ComponentName name,
+        public void createSession(final ITvInputClient client, final String inputId,
                 int seq, int userId) {
             final int callingUid = Binder.getCallingUid();
             final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
@@ -487,28 +526,35 @@
             final long identity = Binder.clearCallingIdentity();
             try {
                 synchronized (mLock) {
+                    UserState userState = getUserStateLocked(resolvedUserId);
+                    ServiceState serviceState = userState.serviceStateMap.get(inputId);
+                    if (serviceState == null) {
+                        serviceState = new ServiceState(
+                                userState.inputMap.get(inputId), resolvedUserId);
+                        userState.serviceStateMap.put(inputId, serviceState);
+                    }
+                    // Send a null token immediately while reconnecting.
+                    if (serviceState.mReconnecting == true) {
+                        sendSessionTokenToClientLocked(client, inputId, null, null, seq, userId);
+                        return;
+                    }
+
                     // Create a new session token and a session state.
                     IBinder sessionToken = new Binder();
-                    SessionState sessionState = new SessionState(name, client, seq, callingUid);
-                    sessionState.session = null;
+                    SessionState sessionState = new SessionState(
+                            sessionToken, inputId, client, seq, callingUid, resolvedUserId);
 
                     // Add them to the global session state map of the current user.
-                    UserState userState = getUserStateLocked(resolvedUserId);
                     userState.sessionStateMap.put(sessionToken, sessionState);
 
                     // Also, add them to the session state map of the current service.
-                    ServiceState serviceState = userState.serviceStateMap.get(name);
-                    if (serviceState == null) {
-                        serviceState = new ServiceState(resolvedUserId);
-                        userState.serviceStateMap.put(name, serviceState);
-                    }
-                    serviceState.sessionTokens.add(sessionToken);
+                    serviceState.mSessionTokens.add(sessionToken);
 
-                    if (serviceState.service != null) {
-                        createSessionInternalLocked(serviceState.service, sessionToken,
+                    if (serviceState.mService != null) {
+                        createSessionInternalLocked(serviceState.mService, sessionToken,
                                 resolvedUserId);
                     } else {
-                        updateServiceConnectionLocked(name, resolvedUserId);
+                        updateServiceConnectionLocked(inputId, resolvedUserId);
                     }
                 }
             } finally {
@@ -524,14 +570,7 @@
             final long identity = Binder.clearCallingIdentity();
             try {
                 synchronized (mLock) {
-                    // Release the session.
-                    try {
-                        getSessionLocked(sessionToken, callingUid, resolvedUserId).release();
-                    } catch (RemoteException e) {
-                        Slog.e(TAG, "error in release", e);
-                    }
-
-                    removeSessionStateLocked(sessionToken, resolvedUserId);
+                    releaseSessionLocked(sessionToken, callingUid, resolvedUserId);
                 }
             } finally {
                 Binder.restoreCallingIdentity(identity);
@@ -599,9 +638,9 @@
                         // Close the open log entry first, if any.
                         UserState userState = getUserStateLocked(resolvedUserId);
                         SessionState sessionState = userState.sessionStateMap.get(sessionToken);
-                        if (sessionState.logUri != null) {
+                        if (sessionState.mLogUri != null) {
                             SomeArgs args = SomeArgs.obtain();
-                            args.arg1 = sessionState.logUri;
+                            args.arg1 = sessionState.mLogUri;
                             args.arg2 = currentTime;
                             mLogHandler.obtainMessage(LogHandler.MSG_CLOSE_ENTRY, args)
                                     .sendToTarget();
@@ -614,10 +653,10 @@
                         values.put(TvContract.WatchedPrograms.WATCH_END_TIME_UTC_MILLIS, 0);
                         values.put(TvContract.WatchedPrograms.CHANNEL_ID, channelId);
 
-                        sessionState.logUri = mContentResolver.insert(
+                        sessionState.mLogUri = mContentResolver.insert(
                                 TvContract.WatchedPrograms.CONTENT_URI, values);
                         SomeArgs args = SomeArgs.obtain();
-                        args.arg1 = sessionState.logUri;
+                        args.arg1 = sessionState.mLogUri;
                         args.arg2 = ContentUris.parseId(channelUri);
                         args.arg3 = currentTime;
                         mLogHandler.obtainMessage(LogHandler.MSG_OPEN_ENTRY, args).sendToTarget();
@@ -694,12 +733,12 @@
     }
 
     private static final class UserState {
-        // A list of all known TV inputs on the system.
-        private final List<TvInputInfo> inputList = new ArrayList<TvInputInfo>();
+        // A mapping from the TV input id to its TvInputInfo.
+        private final Map<String, TvInputInfo> inputMap = new HashMap<String,TvInputInfo>();
 
         // A mapping from the name of a TV input service to its state.
-        private final Map<ComponentName, ServiceState> serviceStateMap =
-                new HashMap<ComponentName, ServiceState>();
+        private final Map<String, ServiceState> serviceStateMap =
+                new HashMap<String, ServiceState>();
 
         // A mapping from the token of a TV input session to its state.
         private final Map<IBinder, SessionState> sessionStateMap =
@@ -707,66 +746,91 @@
     }
 
     private final class ServiceState {
-        private final List<IBinder> clients = new ArrayList<IBinder>();
-        private final List<IBinder> sessionTokens = new ArrayList<IBinder>();
-        private final ServiceConnection connection;
+        // TODO: need to implement DeathRecipient for clients.
+        private final List<IBinder> mClients = new ArrayList<IBinder>();
+        private final List<IBinder> mSessionTokens = new ArrayList<IBinder>();
+        private final ServiceConnection mConnection;
+        private final TvInputInfo mTvInputInfo;
 
-        private ITvInputService service;
-        private ServiceCallback callback;
-        private boolean bound;
-        private boolean available;
+        private ITvInputService mService;
+        private ServiceCallback mCallback;
+        private boolean mBound;
+        private boolean mAvailable;
+        private boolean mReconnecting;
 
-        private ServiceState(int userId) {
-            this.connection = new InputServiceConnection(userId);
+        private ServiceState(TvInputInfo inputInfo, int userId) {
+            mTvInputInfo = inputInfo;
+            mConnection = new InputServiceConnection(inputInfo, userId);
         }
     }
 
-    private static final class SessionState {
-        private final ComponentName name;
-        private final ITvInputClient client;
-        private final int seq;
-        private final int callingUid;
+    private final class SessionState implements IBinder.DeathRecipient {
+        private final String mInputId;
+        private final ITvInputClient mClient;
+        private final int mSeq;
+        private final int mCallingUid;
+        private final int mUserId;
+        private final IBinder mToken;
+        private ITvInputSession mSession;
+        private Uri mLogUri;
 
-        private ITvInputSession session;
-        private Uri logUri;
+        private SessionState(IBinder token, String inputId, ITvInputClient client, int seq,
+                int callingUid, int userId) {
+            mToken = token;
+            mInputId = inputId;
+            mClient = client;
+            mSeq = seq;
+            mCallingUid = callingUid;
+            mUserId = userId;
+        }
 
-        private SessionState(ComponentName name, ITvInputClient client, int seq, int callingUid) {
-            this.name = name;
-            this.client = client;
-            this.seq = seq;
-            this.callingUid = callingUid;
+        @Override
+        public void binderDied() {
+            synchronized (mLock) {
+                mSession = null;
+                if (mClient != null) {
+                    try {
+                        mClient.onSessionReleased(mSeq);
+                    } catch(RemoteException e) {
+                        Slog.e(TAG, "error in onSessionReleased", e);
+                    }
+                }
+                removeSessionStateLocked(mToken, mUserId);
+            }
         }
     }
 
     private final class InputServiceConnection implements ServiceConnection {
+        private final TvInputInfo mTvInputInfo;
         private final int mUserId;
 
-        private InputServiceConnection(int userId) {
+        private InputServiceConnection(TvInputInfo inputInfo, int userId) {
             mUserId = userId;
+            mTvInputInfo = inputInfo;
         }
 
         @Override
         public void onServiceConnected(ComponentName name, IBinder service) {
             if (DEBUG) {
-                Slog.d(TAG, "onServiceConnected(name=" + name.getClassName() + ")");
+                Slog.d(TAG, "onServiceConnected(inputId=" + mTvInputInfo.getId() + ")");
             }
             synchronized (mLock) {
-                ServiceState serviceState = getServiceStateLocked(name, mUserId);
-                serviceState.service = ITvInputService.Stub.asInterface(service);
+                ServiceState serviceState = getServiceStateLocked(mTvInputInfo.getId(), mUserId);
+                serviceState.mService = ITvInputService.Stub.asInterface(service);
 
                 // Register a callback, if we need to.
-                if (!serviceState.clients.isEmpty() && serviceState.callback == null) {
-                    serviceState.callback = new ServiceCallback(mUserId);
+                if (!serviceState.mClients.isEmpty() && serviceState.mCallback == null) {
+                    serviceState.mCallback = new ServiceCallback(mUserId);
                     try {
-                        serviceState.service.registerCallback(serviceState.callback);
+                        serviceState.mService.registerCallback(serviceState.mCallback);
                     } catch (RemoteException e) {
                         Slog.e(TAG, "error in registerCallback", e);
                     }
                 }
 
                 // And create sessions, if any.
-                for (IBinder sessionToken : serviceState.sessionTokens) {
-                    createSessionInternalLocked(serviceState.service, sessionToken, mUserId);
+                for (IBinder sessionToken : serviceState.mSessionTokens) {
+                    createSessionInternalLocked(serviceState.mService, sessionToken, mUserId);
                 }
             }
         }
@@ -774,7 +838,38 @@
         @Override
         public void onServiceDisconnected(ComponentName name) {
             if (DEBUG) {
-                Slog.d(TAG, "onServiceDisconnected(name=" + name.getClassName() + ")");
+                Slog.d(TAG, "onServiceDisconnected(inputId=" + mTvInputInfo.getId() + ")");
+            }
+            if (!mTvInputInfo.getComponent().equals(name)) {
+                throw new IllegalArgumentException("Mismatched ComponentName: "
+                        + mTvInputInfo.getComponent() + " (expected), " + name + " (actual).");
+            }
+            synchronized (mLock) {
+                UserState userState = getUserStateLocked(mUserId);
+                ServiceState serviceState = userState.serviceStateMap.get(mTvInputInfo.getId());
+                if (serviceState != null) {
+                    serviceState.mReconnecting = true;
+                    serviceState.mBound = false;
+                    serviceState.mService = null;
+                    serviceState.mCallback = null;
+
+                    // Send null tokens for not finishing create session events.
+                    for (IBinder sessionToken : serviceState.mSessionTokens) {
+                        SessionState sessionState = userState.sessionStateMap.get(sessionToken);
+                        if (sessionState.mSession == null) {
+                            removeSessionStateLocked(sessionToken, sessionState.mUserId);
+                            sendSessionTokenToClientLocked(sessionState.mClient,
+                                    sessionState.mInputId, null, null, sessionState.mSeq,
+                                    sessionState.mUserId);
+                        }
+                    }
+
+                    if (serviceState.mAvailable) {
+                        serviceState.mAvailable = false;
+                        broadcastServiceAvailabilityChangedLocked(serviceState);
+                    }
+                    updateServiceConnectionLocked(mTvInputInfo.getId(), mUserId);
+                }
             }
         }
     }
@@ -787,18 +882,16 @@
         }
 
         @Override
-        public void onAvailabilityChanged(ComponentName name, boolean isAvailable)
-                throws RemoteException {
+        public void onAvailabilityChanged(String inputId, boolean isAvailable) {
             if (DEBUG) {
-                Slog.d(TAG, "onAvailabilityChanged(name=" + name.getClassName() + ", isAvailable="
+                Slog.d(TAG, "onAvailabilityChanged(inputId=" + inputId + ", isAvailable="
                         + isAvailable + ")");
             }
             synchronized (mLock) {
-                ServiceState serviceState = getServiceStateLocked(name, mUserId);
-                serviceState.available = isAvailable;
-                for (IBinder iBinder : serviceState.clients) {
-                    ITvInputClient client = ITvInputClient.Stub.asInterface(iBinder);
-                    client.onAvailabilityChanged(name, isAvailable);
+                ServiceState serviceState = getServiceStateLocked(inputId, mUserId);
+                if (serviceState.mAvailable != isAvailable) {
+                    serviceState.mAvailable = isAvailable;
+                    broadcastServiceAvailabilityChangedLocked(serviceState);
                 }
             }
         }
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 637beec..836a19c 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -5038,6 +5038,10 @@
             throw new SecurityException("Requires DISABLE_KEYGUARD permission");
         }
 
+        if (token == null) {
+            throw new IllegalArgumentException("token == null");
+        }
+
         mKeyguardDisableHandler.sendMessage(mKeyguardDisableHandler.obtainMessage(
                 KeyguardDisableHandler.KEYGUARD_DISABLE, new Pair<IBinder, String>(token, tag)));
     }
@@ -5049,6 +5053,10 @@
             throw new SecurityException("Requires DISABLE_KEYGUARD permission");
         }
 
+        if (token == null) {
+            throw new IllegalArgumentException("token == null");
+        }
+
         mKeyguardDisableHandler.sendMessage(mKeyguardDisableHandler.obtainMessage(
                 KeyguardDisableHandler.KEYGUARD_REENABLE, token));
     }
@@ -5062,6 +5070,11 @@
             != PackageManager.PERMISSION_GRANTED) {
             throw new SecurityException("Requires DISABLE_KEYGUARD permission");
         }
+
+        if (callback == null) {
+            throw new IllegalArgumentException("callback == null");
+        }
+
         mPolicy.exitKeyguardSecurely(new WindowManagerPolicy.OnKeyguardExitResult() {
             @Override
             public void onKeyguardExitResult(boolean success) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 4e22b2a..1980d1e 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -130,6 +130,7 @@
     private static final boolean DBG = false;
 
     final Context mContext;
+    final UserManager mUserManager;
     final PowerManager.WakeLock mWakeLock;
 
     IPowerManager mIPowerManager;
@@ -209,7 +210,7 @@
                         + action + " for user " + userHandle);
                 mHandler.post(new Runnable() {
                     public void run() {
-                        handlePasswordExpirationNotification(getUserData(userHandle));
+                        handlePasswordExpirationNotification(userHandle);
                     }
                 });
             }
@@ -611,6 +612,7 @@
      */
     public DevicePolicyManagerService(Context context) {
         mContext = context;
+        mUserManager = UserManager.get(mContext);
         mHasFeature = context.getPackageManager().hasSystemFeature(
                 PackageManager.FEATURE_DEVICE_ADMIN);
         mWakeLock = ((PowerManager)context.getSystemService(Context.POWER_SERVICE))
@@ -818,6 +820,9 @@
         sendAdminCommandLocked(admin, action, null);
     }
 
+    /**
+     * Send an update to one specific admin, get notified when that admin returns a result.
+     */
     void sendAdminCommandLocked(ActiveAdmin admin, String action, BroadcastReceiver result) {
         Intent intent = new Intent(action);
         intent.setComponent(admin.info.getComponent());
@@ -832,12 +837,15 @@
         }
     }
 
+    /**
+     * Send an update to all admins of a user that enforce a specified policy.
+     */
     void sendAdminCommandLocked(String action, int reqPolicy, int userHandle) {
         final DevicePolicyData policy = getUserData(userHandle);
         final int count = policy.mAdminList.size();
         if (count > 0) {
             for (int i = 0; i < count; i++) {
-                ActiveAdmin admin = policy.mAdminList.get(i);
+                final ActiveAdmin admin = policy.mAdminList.get(i);
                 if (admin.info.usesPolicy(reqPolicy)) {
                     sendAdminCommandLocked(admin, action);
                 }
@@ -845,6 +853,19 @@
         }
     }
 
+    /**
+     * Send an update intent to all admins of a user and its profiles. Only send to admins that
+     * enforce a specified policy.
+     */
+    private void sendAdminCommandToSelfAndProfilesLocked(String action, int reqPolicy,
+            int userHandle) {
+        List<UserInfo> profiles = mUserManager.getProfiles(userHandle);
+        for (UserInfo ui : profiles) {
+            int id = ui.getUserHandle().getIdentifier();
+            sendAdminCommandLocked(action, reqPolicy, id);
+        }
+    }
+
     void removeActiveAdminLocked(final ComponentName adminReceiver, int userHandle) {
         final ActiveAdmin admin = getActiveAdminUncheckedLocked(adminReceiver, userHandle);
         if (admin != null) {
@@ -1190,23 +1211,29 @@
         }
     }
 
-    private void handlePasswordExpirationNotification(DevicePolicyData policy) {
+    private void handlePasswordExpirationNotification(int userHandle) {
         synchronized (this) {
             final long now = System.currentTimeMillis();
-            final int N = policy.mAdminList.size();
-            if (N <= 0) {
-                return;
-            }
-            for (int i=0; i < N; i++) {
-                ActiveAdmin admin = policy.mAdminList.get(i);
-                if (admin.info.usesPolicy(DeviceAdminInfo.USES_POLICY_EXPIRE_PASSWORD)
-                        && admin.passwordExpirationTimeout > 0L
-                        && admin.passwordExpirationDate > 0L
-                        && now >= admin.passwordExpirationDate - EXPIRATION_GRACE_PERIOD_MS) {
-                    sendAdminCommandLocked(admin, DeviceAdminReceiver.ACTION_PASSWORD_EXPIRING);
+
+            List<UserInfo> profiles = mUserManager.getProfiles(userHandle);
+            for (UserInfo ui : profiles) {
+                int profileUserHandle = ui.getUserHandle().getIdentifier();
+                final DevicePolicyData policy = getUserData(profileUserHandle);
+                final int count = policy.mAdminList.size();
+                if (count > 0) {
+                    for (int i = 0; i < count; i++) {
+                        final ActiveAdmin admin = policy.mAdminList.get(i);
+                        if (admin.info.usesPolicy(DeviceAdminInfo.USES_POLICY_EXPIRE_PASSWORD)
+                                && admin.passwordExpirationTimeout > 0L
+                                && now >= admin.passwordExpirationDate - EXPIRATION_GRACE_PERIOD_MS
+                                && admin.passwordExpirationDate > 0L) {
+                            sendAdminCommandLocked(admin,
+                                    DeviceAdminReceiver.ACTION_PASSWORD_EXPIRING);
+                        }
+                    }
                 }
             }
-            setExpirationAlarmCheckLocked(mContext, policy);
+            setExpirationAlarmCheckLocked(mContext, getUserData(userHandle));
         }
     }
 
@@ -1216,8 +1243,7 @@
         final boolean hasCert = DevicePolicyManager.hasAnyCaCertsInstalled();
         if (! hasCert) {
             if (intent.getAction().equals(KeyChain.ACTION_STORAGE_CHANGED)) {
-                UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
-                for (UserInfo user : um.getUsers()) {
+                for (UserInfo user : mUserManager.getUsers()) {
                     notificationManager.cancelAsUser(
                             null, MONITORING_CERT_NOTIFICATION_ID, user.getUserHandle());
                 }
@@ -1256,8 +1282,7 @@
         // If this is a boot intent, this will fire for each user. But if this is a storage changed
         // intent, it will fire once, so we need to notify all users.
         if (intent.getAction().equals(KeyChain.ACTION_STORAGE_CHANGED)) {
-            UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
-            for (UserInfo user : um.getUsers()) {
+            for (UserInfo user : mUserManager.getUsers()) {
                 notificationManager.notifyAsUser(
                         null, MONITORING_CERT_NOTIFICATION_ID, noti, user.getUserHandle());
             }
@@ -1434,18 +1459,22 @@
         enforceCrossUserPermission(userHandle);
         synchronized (this) {
             int mode = DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
-            DevicePolicyData policy = getUserData(userHandle);
 
             if (who != null) {
                 ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle);
                 return admin != null ? admin.passwordQuality : mode;
             }
 
-            final int N = policy.mAdminList.size();
-            for  (int i=0; i<N; i++) {
-                ActiveAdmin admin = policy.mAdminList.get(i);
-                if (mode < admin.passwordQuality) {
-                    mode = admin.passwordQuality;
+            // Return strictest policy for this user and profiles that are visible from this user.
+            List<UserInfo> profiles = mUserManager.getProfiles(userHandle);
+            for (UserInfo userInfo : profiles) {
+                DevicePolicyData policy = getUserData(userInfo.getUserHandle().getIdentifier());
+                final int N = policy.mAdminList.size();
+                for (int i=0; i<N; i++) {
+                    ActiveAdmin admin = policy.mAdminList.get(i);
+                    if (mode < admin.passwordQuality) {
+                        mode = admin.passwordQuality;
+                    }
                 }
             }
             return mode;
@@ -1476,7 +1505,6 @@
         }
         enforceCrossUserPermission(userHandle);
         synchronized (this) {
-            DevicePolicyData policy = getUserData(userHandle);
             int length = 0;
 
             if (who != null) {
@@ -1484,11 +1512,16 @@
                 return admin != null ? admin.minimumPasswordLength : length;
             }
 
-            final int N = policy.mAdminList.size();
-            for  (int i=0; i<N; i++) {
-                ActiveAdmin admin = policy.mAdminList.get(i);
-                if (length < admin.minimumPasswordLength) {
-                    length = admin.minimumPasswordLength;
+            // Return strictest policy for this user and profiles that are visible from this user.
+            List<UserInfo> profiles = mUserManager.getProfiles(userHandle);
+            for (UserInfo userInfo : profiles) {
+                DevicePolicyData policy = getUserData(userInfo.getUserHandle().getIdentifier());
+                final int N = policy.mAdminList.size();
+                for (int i=0; i<N; i++) {
+                    ActiveAdmin admin = policy.mAdminList.get(i);
+                    if (length < admin.minimumPasswordLength) {
+                        length = admin.minimumPasswordLength;
+                    }
                 }
             }
             return length;
@@ -1519,7 +1552,6 @@
         }
         enforceCrossUserPermission(userHandle);
         synchronized (this) {
-            DevicePolicyData policy = getUserData(userHandle);
             int length = 0;
 
             if (who != null) {
@@ -1527,11 +1559,16 @@
                 return admin != null ? admin.passwordHistoryLength : length;
             }
 
-            final int N = policy.mAdminList.size();
-            for (int i = 0; i < N; i++) {
-                ActiveAdmin admin = policy.mAdminList.get(i);
-                if (length < admin.passwordHistoryLength) {
-                    length = admin.passwordHistoryLength;
+            // Return strictest policy for this user and profiles that are visible from this user.
+            List<UserInfo> profiles = mUserManager.getProfiles(userHandle);
+            for (UserInfo userInfo : profiles) {
+                DevicePolicyData policy = getUserData(userInfo.getUserHandle().getIdentifier());
+                final int N = policy.mAdminList.size();
+                for (int i = 0; i < N; i++) {
+                    ActiveAdmin admin = policy.mAdminList.get(i);
+                    if (length < admin.passwordHistoryLength) {
+                        length = admin.passwordHistoryLength;
+                    }
                 }
             }
             return length;
@@ -1577,19 +1614,23 @@
         }
         enforceCrossUserPermission(userHandle);
         synchronized (this) {
+            long timeout = 0L;
+
             if (who != null) {
                 ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle);
-                return admin != null ? admin.passwordExpirationTimeout : 0L;
+                return admin != null ? admin.passwordExpirationTimeout : timeout;
             }
 
-            long timeout = 0L;
-            DevicePolicyData policy = getUserData(userHandle);
-            final int N = policy.mAdminList.size();
-            for (int i = 0; i < N; i++) {
-                ActiveAdmin admin = policy.mAdminList.get(i);
-                if (timeout == 0L || (admin.passwordExpirationTimeout != 0L
-                        && timeout > admin.passwordExpirationTimeout)) {
-                    timeout = admin.passwordExpirationTimeout;
+            List<UserInfo> profiles = mUserManager.getProfiles(userHandle);
+            for (UserInfo userInfo : profiles) {
+                DevicePolicyData policy = getUserData(userInfo.getUserHandle().getIdentifier());
+                final int N = policy.mAdminList.size();
+                for (int i = 0; i < N; i++) {
+                    ActiveAdmin admin = policy.mAdminList.get(i);
+                    if (timeout == 0L || (admin.passwordExpirationTimeout != 0L
+                            && timeout > admin.passwordExpirationTimeout)) {
+                        timeout = admin.passwordExpirationTimeout;
+                    }
                 }
             }
             return timeout;
@@ -1601,19 +1642,23 @@
      * Returns 0 if not configured.
      */
     private long getPasswordExpirationLocked(ComponentName who, int userHandle) {
+        long timeout = 0L;
+
         if (who != null) {
             ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle);
-            return admin != null ? admin.passwordExpirationDate : 0L;
+            return admin != null ? admin.passwordExpirationDate : timeout;
         }
 
-        long timeout = 0L;
-        DevicePolicyData policy = getUserData(userHandle);
-        final int N = policy.mAdminList.size();
-        for (int i = 0; i < N; i++) {
-            ActiveAdmin admin = policy.mAdminList.get(i);
-            if (timeout == 0L || (admin.passwordExpirationDate != 0
-                    && timeout > admin.passwordExpirationDate)) {
-                timeout = admin.passwordExpirationDate;
+        List<UserInfo> profiles = mUserManager.getProfiles(userHandle);
+        for (UserInfo userInfo : profiles) {
+            DevicePolicyData policy = getUserData(userInfo.getUserHandle().getIdentifier());
+            final int N = policy.mAdminList.size();
+            for (int i = 0; i < N; i++) {
+                ActiveAdmin admin = policy.mAdminList.get(i);
+                if (timeout == 0L || (admin.passwordExpirationDate != 0
+                        && timeout > admin.passwordExpirationDate)) {
+                    timeout = admin.passwordExpirationDate;
+                }
             }
         }
         return timeout;
@@ -1660,12 +1705,16 @@
                 return admin != null ? admin.minimumPasswordUpperCase : length;
             }
 
-            DevicePolicyData policy = getUserData(userHandle);
-            final int N = policy.mAdminList.size();
-            for (int i=0; i<N; i++) {
-                ActiveAdmin admin = policy.mAdminList.get(i);
-                if (length < admin.minimumPasswordUpperCase) {
-                    length = admin.minimumPasswordUpperCase;
+            // Return strictest policy for this user and profiles that are visible from this user.
+            List<UserInfo> profiles = mUserManager.getProfiles(userHandle);
+            for (UserInfo userInfo : profiles) {
+                DevicePolicyData policy = getUserData(userInfo.getUserHandle().getIdentifier());
+                final int N = policy.mAdminList.size();
+                for (int i=0; i<N; i++) {
+                    ActiveAdmin admin = policy.mAdminList.get(i);
+                    if (length < admin.minimumPasswordUpperCase) {
+                        length = admin.minimumPasswordUpperCase;
+                    }
                 }
             }
             return length;
@@ -1700,12 +1749,16 @@
                 return admin != null ? admin.minimumPasswordLowerCase : length;
             }
 
-            DevicePolicyData policy = getUserData(userHandle);
-            final int N = policy.mAdminList.size();
-            for (int i=0; i<N; i++) {
-                ActiveAdmin admin = policy.mAdminList.get(i);
-                if (length < admin.minimumPasswordLowerCase) {
-                    length = admin.minimumPasswordLowerCase;
+            // Return strictest policy for this user and profiles that are visible from this user.
+            List<UserInfo> profiles = mUserManager.getProfiles(userHandle);
+            for (UserInfo userInfo : profiles) {
+                DevicePolicyData policy = getUserData(userInfo.getUserHandle().getIdentifier());
+                final int N = policy.mAdminList.size();
+                for (int i=0; i<N; i++) {
+                    ActiveAdmin admin = policy.mAdminList.get(i);
+                    if (length < admin.minimumPasswordLowerCase) {
+                        length = admin.minimumPasswordLowerCase;
+                    }
                 }
             }
             return length;
@@ -1743,12 +1796,16 @@
                 return admin != null ? admin.minimumPasswordLetters : length;
             }
 
-            DevicePolicyData policy = getUserData(userHandle);
-            final int N = policy.mAdminList.size();
-            for (int i=0; i<N; i++) {
-                ActiveAdmin admin = policy.mAdminList.get(i);
-                if (length < admin.minimumPasswordLetters) {
-                    length = admin.minimumPasswordLetters;
+            // Return strictest policy for this user and profiles that are visible from this user.
+            List<UserInfo> profiles = mUserManager.getProfiles(userHandle);
+            for (UserInfo userInfo : profiles) {
+                DevicePolicyData policy = getUserData(userInfo.getUserHandle().getIdentifier());
+                final int N = policy.mAdminList.size();
+                for (int i=0; i<N; i++) {
+                    ActiveAdmin admin = policy.mAdminList.get(i);
+                    if (length < admin.minimumPasswordLetters) {
+                        length = admin.minimumPasswordLetters;
+                    }
                 }
             }
             return length;
@@ -1786,12 +1843,16 @@
                 return admin != null ? admin.minimumPasswordNumeric : length;
             }
 
-            DevicePolicyData policy = getUserData(userHandle);
-            final int N = policy.mAdminList.size();
-            for (int i = 0; i < N; i++) {
-                ActiveAdmin admin = policy.mAdminList.get(i);
-                if (length < admin.minimumPasswordNumeric) {
-                    length = admin.minimumPasswordNumeric;
+            // Return strictest policy for this user and profiles that are visible from this user.
+            List<UserInfo> profiles = mUserManager.getProfiles(userHandle);
+            for (UserInfo userInfo : profiles) {
+                DevicePolicyData policy = getUserData(userInfo.getUserHandle().getIdentifier());
+                final int N = policy.mAdminList.size();
+                for (int i = 0; i < N; i++) {
+                    ActiveAdmin admin = policy.mAdminList.get(i);
+                    if (length < admin.minimumPasswordNumeric) {
+                        length = admin.minimumPasswordNumeric;
+                    }
                 }
             }
             return length;
@@ -1829,12 +1890,16 @@
                 return admin != null ? admin.minimumPasswordSymbols : length;
             }
 
-            DevicePolicyData policy = getUserData(userHandle);
-            final int N = policy.mAdminList.size();
-            for  (int i=0; i<N; i++) {
-                ActiveAdmin admin = policy.mAdminList.get(i);
-                if (length < admin.minimumPasswordSymbols) {
-                    length = admin.minimumPasswordSymbols;
+            // Return strictest policy for this user and profiles that are visible from this user.
+            List<UserInfo> profiles = mUserManager.getProfiles(userHandle);
+            for (UserInfo userInfo : profiles) {
+                DevicePolicyData policy = getUserData(userInfo.getUserHandle().getIdentifier());
+                final int N = policy.mAdminList.size();
+                for (int i=0; i<N; i++) {
+                    ActiveAdmin admin = policy.mAdminList.get(i);
+                    if (length < admin.minimumPasswordSymbols) {
+                        length = admin.minimumPasswordSymbols;
+                    }
                 }
             }
             return length;
@@ -1872,12 +1937,16 @@
                 return admin != null ? admin.minimumPasswordNonLetter : length;
             }
 
-            DevicePolicyData policy = getUserData(userHandle);
-            final int N = policy.mAdminList.size();
-            for (int i=0; i<N; i++) {
-                ActiveAdmin admin = policy.mAdminList.get(i);
-                if (length < admin.minimumPasswordNonLetter) {
-                    length = admin.minimumPasswordNonLetter;
+            // Return strictest policy for this user and profiles that are visible from this user.
+            List<UserInfo> profiles = mUserManager.getProfiles(userHandle);
+            for (UserInfo userInfo : profiles) {
+                DevicePolicyData policy = getUserData(userInfo.getUserHandle().getIdentifier());
+                final int N = policy.mAdminList.size();
+                for (int i=0; i<N; i++) {
+                    ActiveAdmin admin = policy.mAdminList.get(i);
+                    if (length < admin.minimumPasswordNonLetter) {
+                        length = admin.minimumPasswordNonLetter;
+                    }
                 }
             }
             return length;
@@ -1889,8 +1958,16 @@
             return true;
         }
         enforceCrossUserPermission(userHandle);
+
         synchronized (this) {
-            DevicePolicyData policy = getUserData(userHandle);
+
+            // The active password is stored in the user that runs the launcher
+            // If the user this is called from is part of a profile group, that is the parent
+            // of the group.
+            UserInfo parent = getProfileParent(userHandle);
+            int id = parent == null ? userHandle : parent.id;
+            DevicePolicyData policy = getUserData(id);
+
             // This API can only be called by an active device admin,
             // so try to retrieve it to check that the caller is one.
             getActiveAdminForCallerLocked(null,
@@ -1912,13 +1989,16 @@
     }
 
     public int getCurrentFailedPasswordAttempts(int userHandle) {
-        enforceCrossUserPermission(userHandle);
         synchronized (this) {
             // This API can only be called by an active device admin,
             // so try to retrieve it to check that the caller is one.
             getActiveAdminForCallerLocked(null,
                     DeviceAdminInfo.USES_POLICY_WATCH_LOGIN);
-            return getUserData(userHandle).mFailedPasswordAttempts;
+
+            // The active password is stored in the parent.
+            DevicePolicyData policy = getUserData(getProfileParent(userHandle).id);
+
+            return policy.mFailedPasswordAttempts;
         }
     }
 
@@ -1928,6 +2008,9 @@
         }
         enforceCrossUserPermission(userHandle);
         synchronized (this) {
+            if (who == null) {
+                throw new NullPointerException("ComponentName is null");
+            }
             // This API can only be called by an active device admin,
             // so try to retrieve it to check that the caller is one.
             getActiveAdminForCallerLocked(who,
@@ -1947,7 +2030,6 @@
         }
         enforceCrossUserPermission(userHandle);
         synchronized (this) {
-            DevicePolicyData policy = getUserData(userHandle);
             int count = 0;
 
             if (who != null) {
@@ -1955,14 +2037,19 @@
                 return admin != null ? admin.maximumFailedPasswordsForWipe : count;
             }
 
-            final int N = policy.mAdminList.size();
-            for  (int i=0; i<N; i++) {
-                ActiveAdmin admin = policy.mAdminList.get(i);
-                if (count == 0) {
-                    count = admin.maximumFailedPasswordsForWipe;
-                } else if (admin.maximumFailedPasswordsForWipe != 0
-                        && count > admin.maximumFailedPasswordsForWipe) {
-                    count = admin.maximumFailedPasswordsForWipe;
+            // Return strictest policy for this user and profiles that are visible from this user.
+            List<UserInfo> profiles = mUserManager.getProfiles(userHandle);
+            for (UserInfo userInfo : profiles) {
+                DevicePolicyData policy = getUserData(userInfo.getUserHandle().getIdentifier());
+                final int N = policy.mAdminList.size();
+                for (int i=0; i<N; i++) {
+                    ActiveAdmin admin = policy.mAdminList.get(i);
+                    if (count == 0) {
+                        count = admin.maximumFailedPasswordsForWipe;
+                    } else if (admin.maximumFailedPasswordsForWipe != 0
+                            && count > admin.maximumFailedPasswordsForWipe) {
+                        count = admin.maximumFailedPasswordsForWipe;
+                    }
                 }
             }
             return count;
@@ -1974,9 +2061,11 @@
             return false;
         }
         enforceCrossUserPermission(userHandle);
+        enforceNotManagedProfile(userHandle, "reset the password");
+
         int quality;
         synchronized (this) {
-            // This API can only be called by an active device admin,
+            // This api can only be called by an active device admin,
             // so try to retrieve it to check that the caller is one.
             getActiveAdminForCallerLocked(null,
                     DeviceAdminInfo.USES_POLICY_RESET_PASSWORD);
@@ -2154,15 +2243,19 @@
                 return admin != null ? admin.maximumTimeToUnlock : time;
             }
 
-            DevicePolicyData policy = getUserData(userHandle);
-            final int N = policy.mAdminList.size();
-            for  (int i=0; i<N; i++) {
-                ActiveAdmin admin = policy.mAdminList.get(i);
-                if (time == 0) {
-                    time = admin.maximumTimeToUnlock;
-                } else if (admin.maximumTimeToUnlock != 0
-                        && time > admin.maximumTimeToUnlock) {
-                    time = admin.maximumTimeToUnlock;
+            // Return strictest policy for this user and profiles that are visible from this user.
+            List<UserInfo> profiles = mUserManager.getProfiles(userHandle);
+            for (UserInfo userInfo : profiles) {
+                DevicePolicyData policy = getUserData(userInfo.getUserHandle().getIdentifier());
+                final int N = policy.mAdminList.size();
+                for (int i=0; i<N; i++) {
+                    ActiveAdmin admin = policy.mAdminList.get(i);
+                    if (time == 0) {
+                        time = admin.maximumTimeToUnlock;
+                    } else if (admin.maximumTimeToUnlock != 0
+                            && time > admin.maximumTimeToUnlock) {
+                        time = admin.maximumTimeToUnlock;
+                    }
                 }
             }
             return time;
@@ -2320,7 +2413,7 @@
                 public void run() {
                     try {
                         ActivityManagerNative.getDefault().switchUser(UserHandle.USER_OWNER);
-                        ((UserManager) mContext.getSystemService(Context.USER_SERVICE))
+                        (mUserManager)
                                 .removeUser(userHandle);
                     } catch (RemoteException re) {
                         // Shouldn't happen
@@ -2368,6 +2461,8 @@
             return;
         }
         enforceCrossUserPermission(userHandle);
+        enforceNotManagedProfile(userHandle, "set the active password");
+
         mContext.enforceCallingOrSelfPermission(
                 android.Manifest.permission.BIND_DEVICE_ADMIN, null);
         DevicePolicyData p = getUserData(userHandle);
@@ -2396,7 +2491,8 @@
                     saveSettingsLocked(userHandle);
                     updatePasswordExpirationsLocked(userHandle);
                     setExpirationAlarmCheckLocked(mContext, p);
-                    sendAdminCommandLocked(DeviceAdminReceiver.ACTION_PASSWORD_CHANGED,
+                    sendAdminCommandToSelfAndProfilesLocked(
+                            DeviceAdminReceiver.ACTION_PASSWORD_CHANGED,
                             DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD, userHandle);
                 } finally {
                     Binder.restoreCallingIdentity(ident);
@@ -2406,26 +2502,31 @@
     }
 
     /**
-     * Called any time the device password is updated.  Resets all password expiration clocks.
+     * Called any time the device password is updated. Resets all password expiration clocks.
      */
     private void updatePasswordExpirationsLocked(int userHandle) {
-        DevicePolicyData policy = getUserData(userHandle);
-        final int N = policy.mAdminList.size();
-        if (N > 0) {
-            for (int i=0; i<N; i++) {
-                ActiveAdmin admin = policy.mAdminList.get(i);
-                if (admin.info.usesPolicy(DeviceAdminInfo.USES_POLICY_EXPIRE_PASSWORD)) {
-                    long timeout = admin.passwordExpirationTimeout;
-                    long expiration = timeout > 0L ? (timeout + System.currentTimeMillis()) : 0L;
-                    admin.passwordExpirationDate = expiration;
+            List<UserInfo> profiles = mUserManager.getProfiles(userHandle);
+            for (UserInfo userInfo : profiles) {
+                int profileId = userInfo.getUserHandle().getIdentifier();
+                DevicePolicyData policy = getUserData(profileId);
+                final int N = policy.mAdminList.size();
+                if (N > 0) {
+                    for (int i=0; i<N; i++) {
+                        ActiveAdmin admin = policy.mAdminList.get(i);
+                        if (admin.info.usesPolicy(DeviceAdminInfo.USES_POLICY_EXPIRE_PASSWORD)) {
+                            long timeout = admin.passwordExpirationTimeout;
+                            long expiration = timeout > 0L ? (timeout + System.currentTimeMillis()) : 0L;
+                            admin.passwordExpirationDate = expiration;
+                        }
+                    }
                 }
+                saveSettingsLocked(profileId);
             }
-            saveSettingsLocked(userHandle);
-        }
     }
 
     public void reportFailedPasswordAttempt(int userHandle) {
         enforceCrossUserPermission(userHandle);
+        enforceNotManagedProfile(userHandle, "report failed password attempt");
         mContext.enforceCallingOrSelfPermission(
                 android.Manifest.permission.BIND_DEVICE_ADMIN, null);
 
@@ -2440,7 +2541,8 @@
                     if (max > 0 && policy.mFailedPasswordAttempts >= max) {
                         wipeDeviceOrUserLocked(0, userHandle);
                     }
-                    sendAdminCommandLocked(DeviceAdminReceiver.ACTION_PASSWORD_FAILED,
+                    sendAdminCommandToSelfAndProfilesLocked(
+                            DeviceAdminReceiver.ACTION_PASSWORD_FAILED,
                             DeviceAdminInfo.USES_POLICY_WATCH_LOGIN, userHandle);
                 }
             } finally {
@@ -2463,7 +2565,8 @@
                     policy.mPasswordOwner = -1;
                     saveSettingsLocked(userHandle);
                     if (mHasFeature) {
-                        sendAdminCommandLocked(DeviceAdminReceiver.ACTION_PASSWORD_SUCCEEDED,
+                        sendAdminCommandToSelfAndProfilesLocked(
+                                DeviceAdminReceiver.ACTION_PASSWORD_SUCCEEDED,
                                 DeviceAdminInfo.USES_POLICY_WATCH_LOGIN, userHandle);
                     }
                 } finally {
@@ -2492,7 +2595,7 @@
             // Scan through active admins and find if anyone has already
             // set the global proxy.
             Set<ComponentName> compSet = policy.mAdminMap.keySet();
-            for  (ComponentName component : compSet) {
+            for (ComponentName component : compSet) {
                 ActiveAdmin ap = policy.mAdminMap.get(component);
                 if ((ap.specifiesGlobalProxy) && (!component.equals(who))) {
                     // Another admin already sets the global proxy
@@ -2521,8 +2624,11 @@
             // Reset the global proxy accordingly
             // Do this using system permissions, as apps cannot write to secure settings
             long origId = Binder.clearCallingIdentity();
-            resetGlobalProxyLocked(policy);
-            Binder.restoreCallingIdentity(origId);
+            try {
+                resetGlobalProxyLocked(policy);
+            } finally {
+                Binder.restoreCallingIdentity(origId);
+            }
             return null;
         }
     }
@@ -2907,8 +3013,7 @@
         }
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USERS, null);
 
-        UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
-        if (um.getUserInfo(userHandle) == null) {
+        if (mUserManager.getUserInfo(userHandle) == null) {
             // User doesn't exist.
             throw new IllegalArgumentException(
                     "Attempted to set profile owner for invalid userId: " + userHandle);
@@ -2954,10 +3059,9 @@
             int userId = UserHandle.getCallingUserId();
             Slog.d(LOG_TAG, "Enabling the profile for: " + userId);
 
-            UserManager um = UserManager.get(mContext);
             long id = Binder.clearCallingIdentity();
             try {
-                um.setUserEnabled(userId);
+                mUserManager.setUserEnabled(userId);
                 Intent intent = new Intent(Intent.ACTION_MANAGED_PROFILE_ADDED);
                 intent.putExtra(Intent.EXTRA_USER, new UserHandle(UserHandle.getCallingUserId()));
                 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY |
@@ -3021,6 +3125,30 @@
         }
     }
 
+    private void enforceNotManagedProfile(int userHandle, String message) {
+        if(isManagedProfile(userHandle)) {
+            throw new SecurityException("You can not " + message + " from a managed profile. ");
+        }
+    }
+
+    private UserInfo getProfileParent(int userHandle) {
+        long ident = Binder.clearCallingIdentity();
+        try {
+            return mUserManager.getProfileParent(userHandle);
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
+    private boolean isManagedProfile(int userHandle) {
+        long ident = Binder.clearCallingIdentity();
+        try {
+            return mUserManager.getUserInfo(userHandle).isManagedProfile();
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
     private void enableIfNecessary(String packageName, int userId) {
         try {
             IPackageManager ipm = AppGlobals.getPackageManager();
@@ -3124,10 +3252,9 @@
             }
             getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
 
-            UserManager um = UserManager.get(mContext);
             long id = Binder.clearCallingIdentity();
             try {
-                um.setApplicationRestrictions(packageName, settings, userHandle);
+                mUserManager.setApplicationRestrictions(packageName, settings, userHandle);
             } finally {
                 restoreCallingIdentity(id);
             }
@@ -3191,10 +3318,9 @@
             }
             getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
 
-            UserManager um = UserManager.get(mContext);
             long id = Binder.clearCallingIdentity();
             try {
-                return um.getApplicationRestrictions(packageName, userHandle);
+                return mUserManager.getApplicationRestrictions(packageName, userHandle);
             } finally {
                 restoreCallingIdentity(id);
             }
@@ -3211,10 +3337,9 @@
             }
             getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
 
-            UserManager um = UserManager.get(mContext);
             long id = Binder.clearCallingIdentity();
             try {
-                um.setUserRestriction(key, enabled, userHandle);
+                mUserManager.setUserRestriction(key, enabled, userHandle);
             } finally {
                 restoreCallingIdentity(id);
             }
diff --git a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
index 8ea9b0d..b104c11 100644
--- a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
@@ -18,7 +18,7 @@
 
 import android.content.Intent;
 import android.net.LinkProperties;
-import android.net.LinkCapabilities;
+import android.net.NetworkCapabilities;
 import android.os.Bundle;
 import android.telephony.CellInfo;
 import android.telephony.DataConnectionRealTimeInfo;
@@ -37,7 +37,7 @@
     void notifyDataActivity(int state);
     void notifyDataConnection(int state, boolean isDataConnectivityPossible,
             String reason, String apn, String apnType, in LinkProperties linkProperties,
-            in LinkCapabilities linkCapabilities, int networkType, boolean roaming);
+            in NetworkCapabilities networkCapabilities, int networkType, boolean roaming);
     void notifyDataConnectionFailed(String reason, String apnType);
     void notifyCellLocation(in Bundle cellLocation);
     void notifyOtaspChanged(in int otaspMode);
diff --git a/telephony/java/com/android/internal/telephony/PhoneConstants.java b/telephony/java/com/android/internal/telephony/PhoneConstants.java
index 8c42d25..6ad57cf 100644
--- a/telephony/java/com/android/internal/telephony/PhoneConstants.java
+++ b/telephony/java/com/android/internal/telephony/PhoneConstants.java
@@ -78,7 +78,7 @@
     public static final String DATA_APN_TYPE_KEY = "apnType";
     public static final String DATA_APN_KEY = "apn";
     public static final String DATA_LINK_PROPERTIES_KEY = "linkProperties";
-    public static final String DATA_LINK_CAPABILITIES_KEY = "linkCapabilities";
+    public static final String DATA_NETWORK_CAPABILITIES_KEY = "networkCapabilities";
 
     public static final String DATA_IFACE_NAME_KEY = "iface";
     public static final String NETWORK_UNAVAILABLE_KEY = "networkUnvailable";
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/CirclePropActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/CirclePropActivity.java
index 5c273de1..1d0a806 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/CirclePropActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/CirclePropActivity.java
@@ -16,6 +16,7 @@
 
 package com.android.test.hwui;
 
+import android.animation.TimeInterpolator;
 import android.app.Activity;
 import android.content.Context;
 import android.graphics.Canvas;
@@ -27,6 +28,8 @@
 import android.view.HardwareCanvas;
 import android.view.RenderNodeAnimator;
 import android.view.View;
+import android.view.animation.AccelerateDecelerateInterpolator;
+import android.view.animation.OvershootInterpolator;
 import android.webkit.WebChromeClient;
 import android.webkit.WebView;
 import android.webkit.WebViewClient;
@@ -122,8 +125,11 @@
                     mPaint, RenderNodeAnimator.PAINT_STROKE_WIDTH,
                     RenderNodeAnimator.DELTA_TYPE_ABSOLUTE, mToggle ? 5.0f : 60.0f));
 
+            TimeInterpolator interp = new OvershootInterpolator(3.0f);
             for (int i = 0; i < mRunningAnimations.size(); i++) {
-                mRunningAnimations.get(i).start(this);
+                RenderNodeAnimator anim = mRunningAnimations.get(i);
+                anim.setInterpolator(interp);
+                anim.start(this);
             }
 
             if (mToggle) {
diff --git a/tests/Split/Android.mk b/tests/Split/Android.mk
new file mode 100644
index 0000000..7884d4d
--- /dev/null
+++ b/tests/Split/Android.mk
@@ -0,0 +1,28 @@
+#
+# 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.
+#
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+LOCAL_PACKAGE_NAME := Split
+
+LOCAL_AAPT_FLAGS := --split fr,de
+LOCAL_AAPT_FLAGS += -v
+
+LOCAL_MODULE_TAGS := tests
+
+include $(BUILD_PACKAGE)
diff --git a/tests/Split/AndroidManifest.xml b/tests/Split/AndroidManifest.xml
new file mode 100644
index 0000000..a4956a7
--- /dev/null
+++ b/tests/Split/AndroidManifest.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.example.split">
+    <application android:label="@string/app_title">
+        <activity android:name="ActivityMain">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>
diff --git a/tests/Split/assets/blah.txt b/tests/Split/assets/blah.txt
new file mode 100644
index 0000000..1b37e40
--- /dev/null
+++ b/tests/Split/assets/blah.txt
@@ -0,0 +1 @@
+This is some useful info.
diff --git a/packages/SystemUI/res/drawable/ic_qs_contrast_on.xml b/tests/Split/assets/statement.xml
similarity index 73%
rename from packages/SystemUI/res/drawable/ic_qs_contrast_on.xml
rename to tests/Split/assets/statement.xml
index a018929..91750d1 100644
--- a/packages/SystemUI/res/drawable/ic_qs_contrast_on.xml
+++ b/tests/Split/assets/statement.xml
@@ -1,6 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!--
-     Copyright (C) 2014 The Android Open Source Project
+<!-- 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.
@@ -15,6 +14,6 @@
      limitations under the License.
 -->
 
-<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
-    android:src="@drawable/ic_qs_contrast_alpha"
-    android:tint="@color/ic_qs_on" />
+<statement>
+    <value>Hello</value>
+</statement>
diff --git a/packages/SystemUI/res/drawable/ic_qs_contrast_on.xml b/tests/Split/res/layout-fr-sw600dp/main.xml
similarity index 64%
copy from packages/SystemUI/res/drawable/ic_qs_contrast_on.xml
copy to tests/Split/res/layout-fr-sw600dp/main.xml
index a018929..2461c8c 100644
--- a/packages/SystemUI/res/drawable/ic_qs_contrast_on.xml
+++ b/tests/Split/res/layout-fr-sw600dp/main.xml
@@ -1,6 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!--
-     Copyright (C) 2014 The Android Open Source Project
+<!-- 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.
@@ -15,6 +14,10 @@
      limitations under the License.
 -->
 
-<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
-    android:src="@drawable/ic_qs_contrast_alpha"
-    android:tint="@color/ic_qs_on" />
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+    <FrameLayout
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"/>
+</FrameLayout>
diff --git a/packages/SystemUI/res/drawable/ic_qs_contrast_on.xml b/tests/Split/res/layout/main.xml
similarity index 73%
copy from packages/SystemUI/res/drawable/ic_qs_contrast_on.xml
copy to tests/Split/res/layout/main.xml
index a018929..36992a2 100644
--- a/packages/SystemUI/res/drawable/ic_qs_contrast_on.xml
+++ b/tests/Split/res/layout/main.xml
@@ -1,6 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!--
-     Copyright (C) 2014 The Android Open Source Project
+<!-- 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.
@@ -15,6 +14,6 @@
      limitations under the License.
 -->
 
-<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
-    android:src="@drawable/ic_qs_contrast_alpha"
-    android:tint="@color/ic_qs_on" />
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"/>
diff --git a/packages/SystemUI/res/drawable/ic_qs_contrast_on.xml b/tests/Split/res/values-de/values.xml
similarity index 73%
copy from packages/SystemUI/res/drawable/ic_qs_contrast_on.xml
copy to tests/Split/res/values-de/values.xml
index a018929..26d0507 100644
--- a/packages/SystemUI/res/drawable/ic_qs_contrast_on.xml
+++ b/tests/Split/res/values-de/values.xml
@@ -1,6 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!--
-     Copyright (C) 2014 The Android Open Source Project
+<!-- 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.
@@ -15,6 +14,6 @@
      limitations under the License.
 -->
 
-<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
-    android:src="@drawable/ic_qs_contrast_alpha"
-    android:tint="@color/ic_qs_on" />
+<resources>
+    <string name="test">Achtung!</string>
+</resources>
diff --git a/packages/SystemUI/res/drawable/ic_qs_contrast_on.xml b/tests/Split/res/values-fr/values.xml
similarity index 63%
copy from packages/SystemUI/res/drawable/ic_qs_contrast_on.xml
copy to tests/Split/res/values-fr/values.xml
index a018929..16532da 100644
--- a/packages/SystemUI/res/drawable/ic_qs_contrast_on.xml
+++ b/tests/Split/res/values-fr/values.xml
@@ -1,6 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!--
-     Copyright (C) 2014 The Android Open Source Project
+<!-- 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.
@@ -15,6 +14,12 @@
      limitations under the License.
 -->
 
-<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
-    android:src="@drawable/ic_qs_contrast_alpha"
-    android:tint="@color/ic_qs_on" />
+<resources>
+    <string name="app_title">APK Divisé</string>
+    <string name="test">Bonjour, Monde!</string>
+    <string name="blah">Bleh..</string>
+    <string-array name="lotsofstrings">
+        <item>Hé là</item>
+        <item>Au revoir</item>
+    </string-array>
+</resources>
diff --git a/packages/SystemUI/res/drawable/ic_qs_contrast_on.xml b/tests/Split/res/values-sw600dp/values.xml
similarity index 73%
copy from packages/SystemUI/res/drawable/ic_qs_contrast_on.xml
copy to tests/Split/res/values-sw600dp/values.xml
index a018929..a8329bb 100644
--- a/packages/SystemUI/res/drawable/ic_qs_contrast_on.xml
+++ b/tests/Split/res/values-sw600dp/values.xml
@@ -1,6 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!--
-     Copyright (C) 2014 The Android Open Source Project
+<!-- 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.
@@ -15,6 +14,6 @@
      limitations under the License.
 -->
 
-<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
-    android:src="@drawable/ic_qs_contrast_alpha"
-    android:tint="@color/ic_qs_on" />
+<resources>
+    <dimen name="width">230dp</dimen>
+</resources>
diff --git a/tests/Split/res/values/values.xml b/tests/Split/res/values/values.xml
new file mode 100644
index 0000000..68edc77
--- /dev/null
+++ b/tests/Split/res/values/values.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<resources>
+    <string name="app_title">Split APK</string>
+    <string name="test">Hello, World!</string>
+    <string name="boom">Boom!</string>
+    <string name="blah">Blah...</string>
+    <string-array name="lotsofstrings">
+        <item>Hello there</item>
+        <item>Good bye</item>
+    </string-array>
+
+    <plurals name="plur">
+        <item quantity="zero">I no haz :(</item>
+        <item quantity="one">I haz 1!1! :)</item>
+        <item quantity="many">I haz ALL!</item>
+    </plurals>
+
+    <bool name="que">true</bool>
+    <color name="green">#00FF00</color>
+    <dimen name="width">23dp</dimen>
+    <item type="id" name="identifier" />
+    <integer name="number">123</integer>
+    <integer-array name="numList">
+        <item>1234</item>
+    </integer-array>
+
+    <array name="ary">
+        <item>@string/test</item>
+        <item>@string/boom</item>
+        <item>25dp</item>
+    </array>
+</resources>
diff --git a/tests/Split/src/java/com/android/example/split/ActivityMain.java b/tests/Split/src/java/com/android/example/split/ActivityMain.java
new file mode 100644
index 0000000..a15fb3c
--- /dev/null
+++ b/tests/Split/src/java/com/android/example/split/ActivityMain.java
@@ -0,0 +1,31 @@
+/*
+ * 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.example.split;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.widget.TextView;
+
+public class ActivityMain extends Activity {
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        TextView text = new TextView(this);
+        text.setText(R.string.test);
+        setContentView(text);
+    }
+}
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable01.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable01.xml
index d0f2a2d..118f258 100644
--- a/tests/VectorDrawableTest/res/drawable/vector_drawable01.xml
+++ b/tests/VectorDrawableTest/res/drawable/vector_drawable01.xml
@@ -24,13 +24,12 @@
         android:viewportHeight="480"
         android:viewportWidth="480" />
 
-    <group>
-        <path
-            android:name="box1"
-            android:pathData="m20,200l100,90l180,-180l-35,-35l-145,145l-60,-60l-40,40z"
-            android:fill="?android:attr/colorControlActivated"
-            android:stroke="?android:attr/colorControlActivated"
-            android:strokeLineCap="round"
-            android:strokeLineJoin="round" />
-    </group>
-</vector>
+    <path
+        android:name="box1"
+        android:fill="?android:attr/colorControlActivated"
+        android:pathData="m20,200l100,90l180,-180l-35,-35l-145,145l-60,-60l-40,40z"
+        android:stroke="?android:attr/colorControlActivated"
+        android:strokeLineCap="round"
+        android:strokeLineJoin="round" />
+
+</vector>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable02.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable02.xml
index 728624a..034f7a0 100644
--- a/tests/VectorDrawableTest/res/drawable/vector_drawable02.xml
+++ b/tests/VectorDrawableTest/res/drawable/vector_drawable02.xml
@@ -1,4 +1,5 @@
-<!-- Copyright (C) 2014 The Android Open Source Project
+<!--
+ 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.
@@ -15,23 +16,23 @@
 <vector xmlns:android="http://schemas.android.com/apk/res/android" >
 
     <size
-            android:width="64dp"
-            android:height="64dp"/>
+        android:height="64dp"
+        android:width="64dp" />
 
-    <viewport android:viewportWidth="320"
-          android:viewportHeight="320"/>
+    <viewport
+        android:viewportHeight="320"
+        android:viewportWidth="320" />
 
-    <group>
-        <path
-                android:name="house"
-                android:pathData="M 130,225 L 130,115 L 130,115 L 70,15 L 10,115 L 10,115 L 10,225 z"
-                android:fill="#ff440000"
-                android:stroke="#FF00FF00"
-                android:strokeWidth="10"
-                android:rotation="180"
-                android:pivotX="70"
-                android:pivotY="120"
-                android:trimPathStart=".1"
-                android:trimPathEnd=".9"/>
-    </group>
-</vector>
+    <path
+        android:name="house"
+        android:fill="#ff440000"
+        android:pathData="M 130,225 L 130,115 L 130,115 L 70,15 L 10,115 L 10,115 L 10,225 z"
+        android:pivotX="70"
+        android:pivotY="120"
+        android:rotation="180"
+        android:stroke="#FF00FF00"
+        android:strokeWidth="10"
+        android:trimPathEnd=".9"
+        android:trimPathStart=".1" />
+
+</vector>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable03.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable03.xml
index 1792683..451b28e 100644
--- a/tests/VectorDrawableTest/res/drawable/vector_drawable03.xml
+++ b/tests/VectorDrawableTest/res/drawable/vector_drawable03.xml
@@ -1,4 +1,5 @@
-<!-- Copyright (C) 2014 The Android Open Source Project
+<!--
+ 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.
@@ -15,51 +16,47 @@
 <vector xmlns:android="http://schemas.android.com/apk/res/android" >
 
     <size
-            android:width="64dp"
-            android:height="64dp"/>
+        android:height="64dp"
+        android:width="64dp" />
 
     <viewport
-            android:viewportWidth="7.30625"
-            android:viewportHeight="12.25"/>
+        android:viewportHeight="12.25"
+        android:viewportWidth="7.30625" />
 
-    <group>
-
-        <path
-                android:name="clip1"
-                android:pathData="
+    <path
+        android:name="clip1"
+        android:clipToPath="true"
+        android:pathData="
                 M 0, 0
                 l 7.3, 0
                 l 0, 0
                 l -7.3, 0
                 z"
-                android:clipToPath="true"
-                android:rotation="-30"
-                android:pivotX="3.65"
-                android:pivotY="6.125"
-                />
-        <path
-                android:name="one"
-                android:pathData="M 1.215625,9.5l 1.9375,0.0 0.0,-6.671875 -2.109375,0.421875 0.0,-1.078125
+        android:pivotX="3.65"
+        android:pivotY="6.125"
+        android:rotation="-30" />
+    <path
+        android:name="one"
+        android:fill="#ff88ff"
+        android:pathData="M 1.215625,9.5l 1.9375,0.0 0.0,-6.671875 -2.109375,0.421875 0.0,-1.078125
                 l 2.09375,-0.421875 1.1874998,0.0 0.0,7.75 1.9375,0.0 0.0,1.0
-                l -5.046875,0.0 0.0,-1.0Z"
-                android:fill="#ff88ff"
-                />
-        <path
-                android:name="clip2"
-                android:pathData="
+                l -5.046875,0.0 0.0,-1.0Z" />
+    <path
+        android:name="clip2"
+        android:clipToPath="true"
+        android:pathData="
                 M 0, 0
                 l 7.3, 0
                 l 0, 12.25
                 l -7.3, 0
                 z"
-                android:clipToPath="true"
-                android:rotation="-30"
-                android:pivotX="3.65"
-                android:pivotY="6.125"
-                />
-        <path
-                android:name="two"
-                android:pathData="M 2.534375,9.6875l 4.140625,0.0 0.0,1.0 -5.5625,0.0 0.0,-1.0q 0.671875,-0.6875 1.828125,-1.859375
+        android:pivotX="3.65"
+        android:pivotY="6.125"
+        android:rotation="-30" />
+    <path
+        android:name="two"
+        android:fill="#ff88ff"
+        android:pathData="M 2.534375,9.6875l 4.140625,0.0 0.0,1.0 -5.5625,0.0 0.0,-1.0q 0.671875,-0.6875 1.828125,-1.859375
                         q 1.1718752,-1.1875 1.4687502,-1.53125 0.578125,-0.625 0.796875,-1.0625
                         q 0.234375,-0.453125 0.234375,-0.875 0.0,-0.703125 -0.5,-1.140625
                         q -0.484375,-0.4375 -1.2656252,-0.4375 -0.5625,0.0 -1.1875,0.1875
@@ -67,8 +64,6 @@
                         q 0.625,-0.15625 1.140625,-0.15625 1.3593752,0.0 2.1718752,0.6875
                         q 0.8125,0.671875 0.8125,1.8125 0.0,0.53125 -0.203125,1.015625
                         q -0.203125,0.484375 -0.734375,1.140625 -0.15625,0.171875 -0.9375,0.984375
-                        q -0.78125024,0.8125 -2.2187502,2.265625Z"
-                android:fill="#ff88ff"
-                />
-    </group>
-</vector>
+                        q -0.78125024,0.8125 -2.2187502,2.265625Z" />
+
+</vector>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable04.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable04.xml
index 90694fb..6f9caa8 100644
--- a/tests/VectorDrawableTest/res/drawable/vector_drawable04.xml
+++ b/tests/VectorDrawableTest/res/drawable/vector_drawable04.xml
@@ -1,4 +1,5 @@
-<!-- Copyright (C) 2014 The Android Open Source Project
+<!--
+ 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.
@@ -12,48 +13,44 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<vector xmlns:android="http://schemas.android.com/apk/res/android">
+<vector xmlns:android="http://schemas.android.com/apk/res/android" >
 
     <size
-            android:width="64dp"
-            android:height="64dp"/>
+        android:height="64dp"
+        android:width="64dp" />
 
     <viewport
-            android:viewportWidth="7.30625"
-            android:viewportHeight="12.25"/>
+        android:viewportHeight="12.25"
+        android:viewportWidth="7.30625" />
 
-    <group>
-        <path
-                android:name="clip1"
-                android:pathData="
+    <path
+        android:name="clip1"
+        android:clipToPath="true"
+        android:fill="#112233"
+        android:pathData="
                 M 3.65, 6.125
                 m -.001, 0
                 a .001,.001 0 1,0 .002,0
-                a .001,.001 0 1,0 -.002,0z"
-                android:clipToPath="true"
-                android:fill="#112233"
-                />
-
-        <path
-                android:name="one"
-                android:pathData="M 1.215625,9.5l 1.9375,0.0 0.0,-6.671875 -2.109375,0.421875 0.0,-1.078125
+                a .001,.001 0 1,0 -.002,0z" />
+    <path
+        android:name="one"
+        android:fill="#ff88ff"
+        android:pathData="M 1.215625,9.5l 1.9375,0.0 0.0,-6.671875 -2.109375,0.421875 0.0,-1.078125
                 l 2.09375,-0.421875 1.1874998,0.0 0.0,7.75 1.9375,0.0 0.0,1.0
-                l -5.046875,0.0 0.0,-1.0Z"
-                android:fill="#ff88ff"
-                />
-        <path
-                android:name="clip2"
-                android:pathData="
+                l -5.046875,0.0 0.0,-1.0Z" />
+    <path
+        android:name="clip2"
+        android:clipToPath="true"
+        android:fill="#112233"
+        android:pathData="
                 M 3.65, 6.125
                 m -6, 0
                 a 6,6 0 1,0 12,0
-                a 6,6 0 1,0 -12,0z"
-                android:clipToPath="true"
-                android:fill="#112233"
-                />
-        <path
-                android:name="two"
-                android:pathData="M 2.534375,9.6875l 4.140625,0.0 0.0,1.0 -5.5625,0.0 0.0,-1.0q 0.671875,-0.6875 1.828125,-1.859375
+                a 6,6 0 1,0 -12,0z" />
+    <path
+        android:name="two"
+        android:fill="#ff88ff"
+        android:pathData="M 2.534375,9.6875l 4.140625,0.0 0.0,1.0 -5.5625,0.0 0.0,-1.0q 0.671875,-0.6875 1.828125,-1.859375
                         q 1.1718752,-1.1875 1.4687502,-1.53125 0.578125,-0.625 0.796875,-1.0625
                         q 0.234375,-0.453125 0.234375,-0.875 0.0,-0.703125 -0.5,-1.140625
                         q -0.484375,-0.4375 -1.2656252,-0.4375 -0.5625,0.0 -1.1875,0.1875
@@ -61,8 +58,6 @@
                         q 0.625,-0.15625 1.140625,-0.15625 1.3593752,0.0 2.1718752,0.6875
                         q 0.8125,0.671875 0.8125,1.8125 0.0,0.53125 -0.203125,1.015625
                         q -0.203125,0.484375 -0.734375,1.140625 -0.15625,0.171875 -0.9375,0.984375
-                        q -0.78125024,0.8125 -2.2187502,2.265625Z"
-                android:fill="#ff88ff"
-                />
-    </group>
-</vector>
+                        q -0.78125024,0.8125 -2.2187502,2.265625Z" />
+
+</vector>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable05.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable05.xml
index c6595fa..e6c2557 100644
--- a/tests/VectorDrawableTest/res/drawable/vector_drawable05.xml
+++ b/tests/VectorDrawableTest/res/drawable/vector_drawable05.xml
@@ -23,18 +23,17 @@
         android:viewportHeight="12.25"
         android:viewportWidth="7.30625" />
 
-    <group>
-        <path
-            android:name="one"
-            android:fill="#ffff00"
-            android:pathData="M 1.215625,9.5l 1.9375,0.0 0.0,-6.671875 -2.109375,0.421875 0.0,-1.078125
+    <path
+        android:name="one"
+        android:fill="#ffff00"
+        android:pathData="M 1.215625,9.5l 1.9375,0.0 0.0,-6.671875 -2.109375,0.421875 0.0,-1.078125
                 l 2.09375,-0.421875 1.1874998,0.0 0.0,7.75 1.9375,0.0 0.0,1.0
                 l -5.046875,0.0 0.0,-1.0Z" />
-        <path
-            android:name="two"
-            android:fill="#ffff00"
-            android:fillOpacity="0"
-            android:pathData="M 2.534375,9.6875l 4.140625,0.0 0.0,1.0 -5.5625,0.0 0.0,-1.0q 0.671875,-0.6875 1.828125,-1.859375
+    <path
+        android:name="two"
+        android:fill="#ffff00"
+        android:fillOpacity="0"
+        android:pathData="M 2.534375,9.6875l 4.140625,0.0 0.0,1.0 -5.5625,0.0 0.0,-1.0q 0.671875,-0.6875 1.828125,-1.859375
                         q 1.1718752,-1.1875 1.4687502,-1.53125 0.578125,-0.625 0.796875,-1.0625
                         q 0.234375,-0.453125 0.234375,-0.875 0.0,-0.703125 -0.5,-1.140625
                         q -0.484375,-0.4375 -1.2656252,-0.4375 -0.5625,0.0 -1.1875,0.1875
@@ -43,5 +42,5 @@
                         q 0.8125,0.671875 0.8125,1.8125 0.0,0.53125 -0.203125,1.015625
                         q -0.203125,0.484375 -0.734375,1.140625 -0.15625,0.171875 -0.9375,0.984375
                         q -0.78125024,0.8125 -2.2187502,2.265625Z" />
-    </group>
+
 </vector>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable06.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable06.xml
index 850de28..3f8cc09 100644
--- a/tests/VectorDrawableTest/res/drawable/vector_drawable06.xml
+++ b/tests/VectorDrawableTest/res/drawable/vector_drawable06.xml
@@ -1,4 +1,5 @@
-<!-- Copyright (C) 2014 The Android Open Source Project
+<!--
+ 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.
@@ -15,34 +16,38 @@
 <vector xmlns:android="http://schemas.android.com/apk/res/android" >
 
     <size
-            android:width="64dp"
-            android:height="64dp"/>
+        android:height="64dp"
+        android:width="64dp" />
 
     <viewport
-            android:viewportWidth="700"
-            android:viewportHeight="700"/>
+        android:viewportHeight="700"
+        android:viewportWidth="700" />
 
-    <group>
-        <path android:pathData="M 569.374 461.472L 569.374 160.658L 160.658 160.658L 160.658 461.472L 569.374 461.472z"
-              android:name="path2451"
-              android:stroke="#FF000000"
-              android:strokeWidth="30.65500000000000"/>
-        <path android:pathData="M 365.015 311.066"
-              android:name="path2453"
-              android:stroke="#FF000000"
-              android:strokeWidth="30.655000000000001"/>
-        <path android:pathData="M 164.46 164.49L 340.78 343.158C 353.849 356.328 377.63 356.172 390.423 343.278L 566.622 165.928"
-              android:name="path2455"
-              android:stroke="#FF000000"
-              android:fill="#FFFFFFFF"
-              android:strokeWidth="30.655000000000001"/>
-        <path android:pathData="M 170.515 451.566L 305.61 313.46"
-              android:name="path2457"
-              android:stroke="#000000"
-              android:strokeWidth="30.655000000000001"/>
-        <path android:pathData="M 557.968 449.974L 426.515 315.375"
-              android:name="path2459"
-              android:stroke="#000000"
-              android:strokeWidth="30.655000000000001"/>
-    </group>
-</vector>
+    <path
+        android:name="path2451"
+        android:pathData="M 569.374 461.472L 569.374 160.658L 160.658 160.658L 160.658 461.472L 569.374 461.472z"
+        android:stroke="#FF000000"
+        android:strokeWidth="30.65500000000000" />
+    <path
+        android:name="path2453"
+        android:pathData="M 365.015 311.066"
+        android:stroke="#FF000000"
+        android:strokeWidth="30.655000000000001" />
+    <path
+        android:name="path2455"
+        android:fill="#FFFFFFFF"
+        android:pathData="M 164.46 164.49L 340.78 343.158C 353.849 356.328 377.63 356.172 390.423 343.278L 566.622 165.928"
+        android:stroke="#FF000000"
+        android:strokeWidth="30.655000000000001" />
+    <path
+        android:name="path2457"
+        android:pathData="M 170.515 451.566L 305.61 313.46"
+        android:stroke="#000000"
+        android:strokeWidth="30.655000000000001" />
+    <path
+        android:name="path2459"
+        android:pathData="M 557.968 449.974L 426.515 315.375"
+        android:stroke="#000000"
+        android:strokeWidth="30.655000000000001" />
+
+</vector>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable07.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable07.xml
index 7c7e679..4db5090 100644
--- a/tests/VectorDrawableTest/res/drawable/vector_drawable07.xml
+++ b/tests/VectorDrawableTest/res/drawable/vector_drawable07.xml
@@ -1,4 +1,5 @@
-<!-- Copyright (C) 2014 The Android Open Source Project
+<!--
+ 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.
@@ -12,21 +13,21 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"   >
+<vector xmlns:android="http://schemas.android.com/apk/res/android" >
+
     <size
-            android:width="64dp"
-            android:height="64dp"/>
+        android:height="64dp"
+        android:width="64dp" />
 
-    <viewport android:viewportWidth="140"
-          android:viewportHeight="110"/>
+    <viewport
+        android:viewportHeight="110"
+        android:viewportWidth="140" />
 
-    <group>
-        <path
-                android:name="back"
-                android:pathData="M 20,55 l 35.3,-35.3 7.07,7.07 -35.3,35.3 z
+    <path
+        android:name="back"
+        android:fill="#ffffffff"
+        android:pathData="M 20,55 l 35.3,-35.3 7.07,7.07 -35.3,35.3 z
               M 27,50 l 97,0 0,10 -97,0 z
-              M 20,55 l 7.07,-7.07 35.3,35.3 -7.07,7.07 z"
-                android:fill="#ffffffff"
-                />
-    </group>
-</vector>
+              M 20,55 l 7.07,-7.07 35.3,35.3 -7.07,7.07 z" />
+
+</vector>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable08.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable08.xml
index 59f7459..44ef979 100644
--- a/tests/VectorDrawableTest/res/drawable/vector_drawable08.xml
+++ b/tests/VectorDrawableTest/res/drawable/vector_drawable08.xml
@@ -1,4 +1,5 @@
-<!-- Copyright (C) 2014 The Android Open Source Project
+<!--
+ 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.
@@ -15,20 +16,18 @@
 <vector xmlns:android="http://schemas.android.com/apk/res/android" >
 
     <size
-            android:width="64dp"
-            android:height="64dp"/>
+        android:height="64dp"
+        android:width="64dp" />
 
+    <viewport
+        android:viewportHeight="600"
+        android:viewportWidth="600" />
 
-    <viewport android:viewportWidth="600"
-          android:viewportHeight="600"/>
+    <path
+        android:name="pie1"
+        android:fill="#ffffcc00"
+        android:pathData="M535.441,412.339A280.868,280.868 0 1,1 536.186,161.733L284.493,286.29Z"
+        android:stroke="#FF00FF00"
+        android:strokeWidth="1" />
 
-    <group>
-        <path
-                android:name="pie1"
-                android:pathData="M535.441,412.339A280.868,280.868 0 1,1 536.186,161.733L284.493,286.29Z"
-                android:fill="#ffffcc00"
-                android:stroke="#FF00FF00"
-                android:strokeWidth="1"/>
-    </group>
-
-</vector>
+</vector>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable09.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable09.xml
index 2e379d6..248a143 100644
--- a/tests/VectorDrawableTest/res/drawable/vector_drawable09.xml
+++ b/tests/VectorDrawableTest/res/drawable/vector_drawable09.xml
@@ -23,14 +23,12 @@
         android:viewportHeight="200"
         android:viewportWidth="200" />
 
-    <group>
-        <path
-            android:name="house"
-            android:fill="#ffffffff"
-            android:pathData="M 100,20 l 0,0 0,140 -80,0 z M 100,20 l 0,0 80,140 -80,0 z"
-            android:pivotX="100"
-            android:pivotY="100"
-            android:rotation="90" />
-    </group>
+    <path
+        android:name="house"
+        android:fill="#ffffffff"
+        android:pathData="M 100,20 l 0,0 0,140 -80,0 z M 100,20 l 0,0 80,140 -80,0 z"
+        android:pivotX="100"
+        android:pivotY="100"
+        android:rotation="90" />
 
 </vector>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable10.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable10.xml
index 8484e9e..56c2972 100644
--- a/tests/VectorDrawableTest/res/drawable/vector_drawable10.xml
+++ b/tests/VectorDrawableTest/res/drawable/vector_drawable10.xml
@@ -21,26 +21,24 @@
         android:width="64dp" />
 
     <viewport
-        android:viewportWidth="200"
-        android:viewportHeight="200"/>
+        android:viewportHeight="200"
+        android:viewportWidth="200" />
 
-    <group>
-        <path
-            android:name="bar3"
-            android:fill="#FFFFFFFF"
-            android:pathData="M49.001,60c-5.466,0 -9.899,4.478 -9.899,10s4.434,10,9.899,10c5.468,0,9.899 -4.478,9.899 -10S54.469,60,49.001,60z" />
-        <path
-            android:name="bar2"
-            android:fill="#FFFFFFFF"
-            android:pathData="M28.001,48.787l7,7.07c7.731 -7.811,20.269 -7.81,28.001,0l6.999 -7.07C58.403,37.071,39.599,37.071,28.001,48.787z" />
-        <path
-            android:name="bar1"
-            android:fill="#FF555555"
-            android:pathData="M14.001,34.645   L21,41.716c15.464 -15.621,40.536 -15.621,56,0l7.001 -7.071C64.672,15.119,33.33,15.119,14.001,34.645z" />
-        <path
-            android:name="bar0"
-            android:fill="#FF555555"
-            android:pathData="M0,20.502l6.999,7.071   c23.196 -23.431,60.806 -23.431,84.002,0L98,20.503C70.938 -6.834,27.063 -6.834,0,20.502z" />
-    </group>
+    <path
+        android:name="bar3"
+        android:fill="#FFFFFFFF"
+        android:pathData="M49.001,60c-5.466,0 -9.899,4.478 -9.899,10s4.434,10,9.899,10c5.468,0,9.899 -4.478,9.899 -10S54.469,60,49.001,60z" />
+    <path
+        android:name="bar2"
+        android:fill="#FFFFFFFF"
+        android:pathData="M28.001,48.787l7,7.07c7.731 -7.811,20.269 -7.81,28.001,0l6.999 -7.07C58.403,37.071,39.599,37.071,28.001,48.787z" />
+    <path
+        android:name="bar1"
+        android:fill="#FF555555"
+        android:pathData="M14.001,34.645   L21,41.716c15.464 -15.621,40.536 -15.621,56,0l7.001 -7.071C64.672,15.119,33.33,15.119,14.001,34.645z" />
+    <path
+        android:name="bar0"
+        android:fill="#FF555555"
+        android:pathData="M0,20.502l6.999,7.071   c23.196 -23.431,60.806 -23.431,84.002,0L98,20.503C70.938 -6.834,27.063 -6.834,0,20.502z" />
 
 </vector>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable11.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable11.xml
index 2b6c5d3..16d8b48 100644
--- a/tests/VectorDrawableTest/res/drawable/vector_drawable11.xml
+++ b/tests/VectorDrawableTest/res/drawable/vector_drawable11.xml
@@ -23,18 +23,16 @@
         android:viewportHeight="80"
         android:viewportWidth="40" />
 
-    <group>
-        <path
-            android:name="battery"
-            android:fill="#3388ff"
-            android:pathData="M 20.28125,2.0000002 C 17.352748,2.0000002 15,4.3527485 15,7.2812502 L 15,8.0000002 L 13.15625,8.0000002 C 9.7507553,8.0000002 7,10.750759 7,14.15625 L 7,39.84375 C 7,43.24924 9.7507558,46 13.15625,46 L 33.84375,46 C 37.249245,46 39.999999,43.24924 40,39.84375 L 40,14.15625 C 40,10.75076 37.249243,8.0000002 33.84375,8.0000002 L 32,8.0000002 L 32,7.2812502 C 32,4.3527485 29.647252,2.0000002 26.71875,2.0000002 L 20.28125,2.0000002 z"
-            android:rotation="0"
-            android:stroke="#ff8833"
-            android:strokeWidth="1" />
-        <path
-            android:name="spark"
-            android:fill="#FFFF0000"
-            android:pathData="M 30,18.031528 L 25.579581,23.421071 L 29.370621,26.765348 L 20.096792,37 L 21.156922,28.014053 L 17,24.902844 L 20.880632,18 L 30,18.031528 z" />
-    </group>
+    <path
+        android:name="battery"
+        android:fill="#3388ff"
+        android:pathData="M 20.28125,2.0000002 C 17.352748,2.0000002 15,4.3527485 15,7.2812502 L 15,8.0000002 L 13.15625,8.0000002 C 9.7507553,8.0000002 7,10.750759 7,14.15625 L 7,39.84375 C 7,43.24924 9.7507558,46 13.15625,46 L 33.84375,46 C 37.249245,46 39.999999,43.24924 40,39.84375 L 40,14.15625 C 40,10.75076 37.249243,8.0000002 33.84375,8.0000002 L 32,8.0000002 L 32,7.2812502 C 32,4.3527485 29.647252,2.0000002 26.71875,2.0000002 L 20.28125,2.0000002 z"
+        android:rotation="0"
+        android:stroke="#ff8833"
+        android:strokeWidth="1" />
+    <path
+        android:name="spark"
+        android:fill="#FFFF0000"
+        android:pathData="M 30,18.031528 L 25.579581,23.421071 L 29.370621,26.765348 L 20.096792,37 L 21.156922,28.014053 L 17,24.902844 L 20.880632,18 L 30,18.031528 z" />
 
 </vector>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable12.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable12.xml
index 681eb4f..0a0407d 100644
--- a/tests/VectorDrawableTest/res/drawable/vector_drawable12.xml
+++ b/tests/VectorDrawableTest/res/drawable/vector_drawable12.xml
@@ -23,22 +23,20 @@
         android:viewportHeight="600"
         android:viewportWidth="600" />
 
-    <group>
-        <path
-            android:name="pie1"
-            android:pathData="M300,70 a230,230 0 1,0 1,0 z"
-            android:stroke="#FF00FF00"
-            android:strokeWidth="70"
-            android:trimPathEnd=".75"
-            android:trimPathOffset="0"
-            android:trimPathStart="0" />
-        <path
-            android:name="v"
-            android:fill="#FF00FF00"
-            android:pathData="M300,70 l 0,-70 70,70 -70,70z"
-            android:pivotX="300"
-            android:pivotY="300"
-            android:rotation="0" />
-    </group>
+    <path
+        android:name="pie1"
+        android:pathData="M300,70 a230,230 0 1,0 1,0 z"
+        android:stroke="#FF00FF00"
+        android:strokeWidth="70"
+        android:trimPathEnd=".75"
+        android:trimPathOffset="0"
+        android:trimPathStart="0" />
+    <path
+        android:name="v"
+        android:fill="#FF00FF00"
+        android:pathData="M300,70 l 0,-70 70,70 -70,70z"
+        android:pivotX="300"
+        android:pivotY="300"
+        android:rotation="0" />
 
 </vector>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable13.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable13.xml
index ef1b8e4..385b1e9 100644
--- a/tests/VectorDrawableTest/res/drawable/vector_drawable13.xml
+++ b/tests/VectorDrawableTest/res/drawable/vector_drawable13.xml
@@ -23,22 +23,20 @@
         android:viewportHeight="400"
         android:viewportWidth="600" />
 
-    <group>
-        <path
-            android:name="pie1"
-            android:fill="#ffffffff"
-            android:pathData="M300,200 h-150 a150,150 0 1,0 150,-150 z"
-            android:stroke="#FF00FF00"
-            android:strokeWidth="1" />
-        <path
-            android:name="half"
-            android:fill="#FFFF0000"
-            android:pathData="M275,175 v-150 a150,150 0 0,0 -150,150 z"
-            android:pivotX="300"
-            android:pivotY="200"
-            android:rotation="0"
-            android:stroke="#FF0000FF"
-            android:strokeWidth="5" />
-    </group>
+    <path
+        android:name="pie1"
+        android:fill="#ffffffff"
+        android:pathData="M300,200 h-150 a150,150 0 1,0 150,-150 z"
+        android:stroke="#FF00FF00"
+        android:strokeWidth="1" />
+    <path
+        android:name="half"
+        android:fill="#FFFF0000"
+        android:pathData="M275,175 v-150 a150,150 0 0,0 -150,150 z"
+        android:pivotX="300"
+        android:pivotY="200"
+        android:rotation="0"
+        android:stroke="#FF0000FF"
+        android:strokeWidth="5" />
 
 </vector>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable14.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable14.xml
index 77bf723..b701b35 100644
--- a/tests/VectorDrawableTest/res/drawable/vector_drawable14.xml
+++ b/tests/VectorDrawableTest/res/drawable/vector_drawable14.xml
@@ -23,19 +23,17 @@
         android:viewportHeight="500"
         android:viewportWidth="800" />
 
-    <group>
-        <path
-            android:name="pie2"
-            android:pathData="M200,350 l 50,-25
+    <path
+        android:name="pie2"
+        android:pathData="M200,350 l 50,-25
            a25,12 -30 0,1 100,-50 l 50,-25
            a25,25 -30 0,1 100,-50 l 50,-25
            a25,37 -30 0,1 100,-50 l 50,-25
            a25,50 -30 0,1 100,-50 l 50,-25"
-            android:pivotX="90"
-            android:pivotY="100"
-            android:rotation="20"
-            android:stroke="#FF00FF00"
-            android:strokeWidth="10" />
-    </group>
+        android:pivotX="90"
+        android:pivotY="100"
+        android:rotation="20"
+        android:stroke="#FF00FF00"
+        android:strokeWidth="10" />
 
 </vector>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable15.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable15.xml
index df5838c..8d773e1 100644
--- a/tests/VectorDrawableTest/res/drawable/vector_drawable15.xml
+++ b/tests/VectorDrawableTest/res/drawable/vector_drawable15.xml
@@ -23,16 +23,14 @@
         android:viewportHeight="400"
         android:viewportWidth="500" />
 
-    <group>
-        <path
-            android:name="house"
-            android:fill="#ff440000"
-            android:pathData="M100,200 C100,100 250,100 250,200 S400,300 400,200"
-            android:pivotX="250"
-            android:pivotY="200"
-            android:rotation="180"
-            android:stroke="#FFFF0000"
-            android:strokeWidth="10" />
-    </group>
+    <path
+        android:name="house"
+        android:fill="#ff440000"
+        android:pathData="M100,200 C100,100 250,100 250,200 S400,300 400,200"
+        android:pivotX="250"
+        android:pivotY="200"
+        android:rotation="180"
+        android:stroke="#FFFF0000"
+        android:strokeWidth="10" />
 
 </vector>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable16.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable16.xml
index 0bdcda5..3b7926c 100644
--- a/tests/VectorDrawableTest/res/drawable/vector_drawable16.xml
+++ b/tests/VectorDrawableTest/res/drawable/vector_drawable16.xml
@@ -23,15 +23,13 @@
         android:viewportHeight="200"
         android:viewportWidth="200" />
 
-    <group>
-        <path
-            android:name="house"
-            android:pathData="M 100,10 v 90 M 10,100 h 90"
-            android:pivotX="100"
-            android:pivotY="100"
-            android:rotation="360"
-            android:stroke="#FF00FF00"
-            android:strokeWidth="10" />
-    </group>
+    <path
+        android:name="house"
+        android:pathData="M 100,10 v 90 M 10,100 h 90"
+        android:pivotX="100"
+        android:pivotY="100"
+        android:rotation="360"
+        android:stroke="#FF00FF00"
+        android:strokeWidth="10" />
 
 </vector>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable17.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable17.xml
index 4453ae4..1ec72be 100644
--- a/tests/VectorDrawableTest/res/drawable/vector_drawable17.xml
+++ b/tests/VectorDrawableTest/res/drawable/vector_drawable17.xml
@@ -1,4 +1,5 @@
-<!-- Copyright (C) 2014 The Android Open Source Project
+<!--
+ 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.
@@ -15,21 +16,20 @@
 <vector xmlns:android="http://schemas.android.com/apk/res/android" >
 
     <size
-            android:width="64dp"
-            android:height="64dp"/>
+        android:height="64dp"
+        android:width="64dp" />
 
-    <viewport android:viewportWidth="1200"
-          android:viewportHeight="600"/>
+    <viewport
+        android:viewportHeight="600"
+        android:viewportWidth="1200" />
 
-    <group>
-        <path
-                android:name="house"
-                android:pathData="M200,300 Q400,50 600,300 T1000,300"
-                android:stroke="#FFFF0000"
-                android:strokeWidth="10"
-                android:rotation="360"
-                android:pivotX="600"
-                android:pivotY="300"/>
-    </group>
+    <path
+        android:name="house"
+        android:pathData="M200,300 Q400,50 600,300 T1000,300"
+        android:pivotX="600"
+        android:pivotY="300"
+        android:rotation="360"
+        android:stroke="#FFFF0000"
+        android:strokeWidth="10" />
 
-</vector>
+</vector>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable18.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable18.xml
index dfae9ac..12d0e93 100644
--- a/tests/VectorDrawableTest/res/drawable/vector_drawable18.xml
+++ b/tests/VectorDrawableTest/res/drawable/vector_drawable18.xml
@@ -23,15 +23,13 @@
         android:viewportHeight="400"
         android:viewportWidth="500" />
 
-    <group>
-        <path
-            android:name="house"
-            android:pathData="M100,200 C100,100 250,100 250,200 S400,300 400,200"
-            android:pivotX="250"
-            android:pivotY="200"
-            android:rotation="360"
-            android:stroke="#FFFFFF00"
-            android:strokeWidth="10" />
-    </group>
+    <path
+        android:name="house"
+        android:pathData="M100,200 C100,100 250,100 250,200 S400,300 400,200"
+        android:pivotX="250"
+        android:pivotY="200"
+        android:rotation="360"
+        android:stroke="#FFFFFF00"
+        android:strokeWidth="10" />
 
 </vector>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable19.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable19.xml
index a890fd6..017e04c 100644
--- a/tests/VectorDrawableTest/res/drawable/vector_drawable19.xml
+++ b/tests/VectorDrawableTest/res/drawable/vector_drawable19.xml
@@ -23,14 +23,12 @@
         android:viewportHeight="800"
         android:viewportWidth="1000" />
 
-    <group>
-        <path
-            android:name="house"
-            android:pathData="M10,300 Q400,550 600,300 T1000,300"
-            android:pivotX="90"
-            android:pivotY="100"
-            android:stroke="#FFFF0000"
-            android:strokeWidth="60" />
-    </group>
+    <path
+        android:name="house"
+        android:pathData="M10,300 Q400,550 600,300 T1000,300"
+        android:pivotX="90"
+        android:pivotY="100"
+        android:stroke="#FFFF0000"
+        android:strokeWidth="60" />
 
 </vector>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable20.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable20.xml
index b8af7e2..b7002a3 100644
--- a/tests/VectorDrawableTest/res/drawable/vector_drawable20.xml
+++ b/tests/VectorDrawableTest/res/drawable/vector_drawable20.xml
@@ -23,16 +23,14 @@
         android:viewportHeight="480"
         android:viewportWidth="480" />
 
-    <group>
-        <path
-            android:name="edit"
-            android:fill="#FF00FFFF"
-            android:pathData="M406.667,180c0,0 -100 -100 -113.334 -113.333
+    <path
+        android:name="edit"
+        android:fill="#FF00FFFF"
+        android:pathData="M406.667,180c0,0 -100 -100 -113.334 -113.333
     c-13.333 -13.334 -33.333,0 -33.333,0l-160,160c0,0 -40,153.333 -40,173.333c0,13.333,13.333,13.333,13.333,13.333l173.334 -40
     c0,0,146.666 -146.666,160 -160C420,200,406.667,180,406.667,180z M226.399,356.823L131.95,378.62l-38.516 -38.522
     c7.848 -34.675,20.152 -82.52,23.538 -95.593l3.027,2.162l106.667,106.666L226.399,356.823z"
-            android:stroke="#FF000000"
-            android:strokeWidth="10" />
-    </group>
+        android:stroke="#FF000000"
+        android:strokeWidth="10" />
 
 </vector>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/drawable/vector_icon_create.xml b/tests/VectorDrawableTest/res/drawable/vector_icon_create.xml
index 22ce795..cda213d 100644
--- a/tests/VectorDrawableTest/res/drawable/vector_icon_create.xml
+++ b/tests/VectorDrawableTest/res/drawable/vector_icon_create.xml
@@ -23,10 +23,8 @@
         android:viewportHeight="24"
         android:viewportWidth="24" />
 
-    <group>
-        <path
-            android:fill="#FF000000"
-            android:pathData="M3.0,17.25L3.0,21.0l3.75,0.0L17.813995,9.936001l-3.75,-3.75L3.0,17.25zM20.707,7.0429993c0.391,-0.391 0.391,-1.023 0.0,-1.414l-2.336,-2.336c-0.391,-0.391 -1.023,-0.391 -1.414,0.0l-1.832,1.832l3.75,3.75L20.707,7.0429993z" />
-    </group>
+    <path
+        android:fill="#FF000000"
+        android:pathData="M3.0,17.25L3.0,21.0l3.75,0.0L17.813995,9.936001l-3.75,-3.75L3.0,17.25zM20.707,7.0429993c0.391,-0.391 0.391,-1.023 0.0,-1.414l-2.336,-2.336c-0.391,-0.391 -1.023,-0.391 -1.414,0.0l-1.832,1.832l3.75,3.75L20.707,7.0429993z" />
 
 </vector>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/drawable/vector_icon_delete.xml b/tests/VectorDrawableTest/res/drawable/vector_icon_delete.xml
index 042173c..2cb6381 100644
--- a/tests/VectorDrawableTest/res/drawable/vector_icon_delete.xml
+++ b/tests/VectorDrawableTest/res/drawable/vector_icon_delete.xml
@@ -23,10 +23,8 @@
         android:viewportHeight="24"
         android:viewportWidth="24" />
 
-    <group>
-        <path
-            android:fill="#FF000000"
-            android:pathData="M6.0,19.0c0.0,1.104 0.896,2.0 2.0,2.0l8.0,0.0c1.104,0.0 2.0,-0.896 2.0,-2.0l0.0,-12.0L6.0,7.0L6.0,19.0zM18.0,4.0l-2.5,0.0l-1.0,-1.0l-5.0,0.0l-1.0,1.0L6.0,4.0C5.4469986,4.0 5.0,4.4469986 5.0,5.0l0.0,1.0l14.0,0.0l0.0,-1.0C19.0,4.4469986 18.552002,4.0 18.0,4.0z" />
-    </group>
+    <path
+        android:fill="#FF000000"
+        android:pathData="M6.0,19.0c0.0,1.104 0.896,2.0 2.0,2.0l8.0,0.0c1.104,0.0 2.0,-0.896 2.0,-2.0l0.0,-12.0L6.0,7.0L6.0,19.0zM18.0,4.0l-2.5,0.0l-1.0,-1.0l-5.0,0.0l-1.0,1.0L6.0,4.0C5.4469986,4.0 5.0,4.4469986 5.0,5.0l0.0,1.0l14.0,0.0l0.0,-1.0C19.0,4.4469986 18.552002,4.0 18.0,4.0z" />
 
 </vector>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/drawable/vector_icon_heart.xml b/tests/VectorDrawableTest/res/drawable/vector_icon_heart.xml
index 6b6f43d..d58942e 100644
--- a/tests/VectorDrawableTest/res/drawable/vector_icon_heart.xml
+++ b/tests/VectorDrawableTest/res/drawable/vector_icon_heart.xml
@@ -23,10 +23,8 @@
         android:viewportHeight="24"
         android:viewportWidth="24" />
 
-    <group>
-        <path
-            android:fill="#FF000000"
-            android:pathData="M16.0,5.0c-1.955,0.0 -3.83,1.268 -4.5,3.0c-0.67,-1.732 -2.547,-3.0 -4.5,-3.0C4.4570007,5.0 2.5,6.931999 2.5,9.5c0.0,3.529 3.793,6.258 9.0,11.5c5.207,-5.242 9.0,-7.971 9.0,-11.5C20.5,6.931999 18.543,5.0 16.0,5.0z" />
-    </group>
+    <path
+        android:fill="#FF000000"
+        android:pathData="M16.0,5.0c-1.955,0.0 -3.83,1.268 -4.5,3.0c-0.67,-1.732 -2.547,-3.0 -4.5,-3.0C4.4570007,5.0 2.5,6.931999 2.5,9.5c0.0,3.529 3.793,6.258 9.0,11.5c5.207,-5.242 9.0,-7.971 9.0,-11.5C20.5,6.931999 18.543,5.0 16.0,5.0z" />
 
 </vector>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/drawable/vector_icon_schedule.xml b/tests/VectorDrawableTest/res/drawable/vector_icon_schedule.xml
index ba8ebca..4717be4 100644
--- a/tests/VectorDrawableTest/res/drawable/vector_icon_schedule.xml
+++ b/tests/VectorDrawableTest/res/drawable/vector_icon_schedule.xml
@@ -23,13 +23,11 @@
         android:viewportHeight="24"
         android:viewportWidth="24" />
 
-    <group>
-        <path
-            android:fillOpacity="0.9"
-            android:pathData="M11.994999,2.0C6.4679985,2.0 2.0,6.4780006 2.0,12.0s4.468,10.0 9.995,10.0S22.0,17.522 22.0,12.0S17.521,2.0 11.994999,2.0zM12.0,20.0c-4.42,0.0 -8.0,-3.582 -8.0,-8.0s3.58,-8.0 8.0,-8.0s8.0,3.582 8.0,8.0S16.419998,20.0 12.0,20.0z" />
-        <path
-            android:fillOpacity="0.9"
-            android:pathData="M12.5,6.0l-1.5,0.0 0.0,7.0 5.3029995,3.1819992 0.75,-1.249999 -4.5529995,-2.7320004z" />
-    </group>
+    <path
+        android:fillOpacity="0.9"
+        android:pathData="M11.994999,2.0C6.4679985,2.0 2.0,6.4780006 2.0,12.0s4.468,10.0 9.995,10.0S22.0,17.522 22.0,12.0S17.521,2.0 11.994999,2.0zM12.0,20.0c-4.42,0.0 -8.0,-3.582 -8.0,-8.0s3.58,-8.0 8.0,-8.0s8.0,3.582 8.0,8.0S16.419998,20.0 12.0,20.0z" />
+    <path
+        android:fillOpacity="0.9"
+        android:pathData="M12.5,6.0l-1.5,0.0 0.0,7.0 5.3029995,3.1819992 0.75,-1.249999 -4.5529995,-2.7320004z" />
 
 </vector>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/drawable/vector_icon_settings.xml b/tests/VectorDrawableTest/res/drawable/vector_icon_settings.xml
index 896a938..c626325 100644
--- a/tests/VectorDrawableTest/res/drawable/vector_icon_settings.xml
+++ b/tests/VectorDrawableTest/res/drawable/vector_icon_settings.xml
@@ -23,10 +23,8 @@
         android:viewportHeight="24"
         android:viewportWidth="24" />
 
-    <group>
-        <path
-            android:fill="#FF000000"
-            android:pathData="M19.429,12.975998c0.042,-0.32 0.07,-0.645 0.07,-0.976s-0.029,-0.655 -0.07,-0.976l2.113,-1.654c0.188,-0.151 0.243,-0.422 0.118,-0.639l-2.0,-3.463c-0.125,-0.217 -0.386,-0.304 -0.612,-0.218l-2.49,1.004c-0.516,-0.396 -1.081,-0.731 -1.69,-0.984l-0.375,-2.648C14.456,2.1829987 14.25,2.0 14.0,2.0l-4.0,0.0C9.75,2.0 9.544,2.1829987 9.506,2.422001L9.131,5.0699997C8.521,5.322998 7.957,5.6570015 7.44,6.054001L4.952,5.0509987C4.726,4.965 4.464,5.052002 4.34,5.269001l-2.0,3.463C2.2150002,8.947998 2.27,9.219002 2.4580002,9.369999l2.112,1.653C4.528,11.344002 4.5,11.668999 4.5,12.0s0.029,0.656 0.071,0.977L2.4580002,14.630001c-0.188,0.151 -0.243,0.422 -0.118,0.639l2.0,3.463c0.125,0.217 0.386,0.304 0.612,0.218l2.489,-1.004c0.516,0.396 1.081,0.731 1.69,0.984l0.375,2.648C9.544,21.817001 9.75,22.0 10.0,22.0l4.0,0.0c0.25,0.0 0.456,-0.183 0.494,-0.422l0.375,-2.648c0.609,-0.253 1.174,-0.588 1.689,-0.984l2.49,1.004c0.226,0.086 0.487,-0.001 0.612,-0.218l2.0,-3.463c0.125,-0.217 0.07,-0.487 -0.118,-0.639L19.429,12.975998zM12.0,16.0c-2.21,0.0 -4.0,-1.791 -4.0,-4.0c0.0,-2.21 1.79,-4.0 4.0,-4.0c2.208,0.0 4.0,1.79 4.0,4.0C16.0,14.209 14.208,16.0 12.0,16.0z" />
-    </group>
+    <path
+        android:fill="#FF000000"
+        android:pathData="M19.429,12.975998c0.042,-0.32 0.07,-0.645 0.07,-0.976s-0.029,-0.655 -0.07,-0.976l2.113,-1.654c0.188,-0.151 0.243,-0.422 0.118,-0.639l-2.0,-3.463c-0.125,-0.217 -0.386,-0.304 -0.612,-0.218l-2.49,1.004c-0.516,-0.396 -1.081,-0.731 -1.69,-0.984l-0.375,-2.648C14.456,2.1829987 14.25,2.0 14.0,2.0l-4.0,0.0C9.75,2.0 9.544,2.1829987 9.506,2.422001L9.131,5.0699997C8.521,5.322998 7.957,5.6570015 7.44,6.054001L4.952,5.0509987C4.726,4.965 4.464,5.052002 4.34,5.269001l-2.0,3.463C2.2150002,8.947998 2.27,9.219002 2.4580002,9.369999l2.112,1.653C4.528,11.344002 4.5,11.668999 4.5,12.0s0.029,0.656 0.071,0.977L2.4580002,14.630001c-0.188,0.151 -0.243,0.422 -0.118,0.639l2.0,3.463c0.125,0.217 0.386,0.304 0.612,0.218l2.489,-1.004c0.516,0.396 1.081,0.731 1.69,0.984l0.375,2.648C9.544,21.817001 9.75,22.0 10.0,22.0l4.0,0.0c0.25,0.0 0.456,-0.183 0.494,-0.422l0.375,-2.648c0.609,-0.253 1.174,-0.588 1.689,-0.984l2.49,1.004c0.226,0.086 0.487,-0.001 0.612,-0.218l2.0,-3.463c0.125,-0.217 0.07,-0.487 -0.118,-0.639L19.429,12.975998zM12.0,16.0c-2.21,0.0 -4.0,-1.791 -4.0,-4.0c0.0,-2.21 1.79,-4.0 4.0,-4.0c2.208,0.0 4.0,1.79 4.0,4.0C16.0,14.209 14.208,16.0 12.0,16.0z" />
 
 </vector>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/drawable/vector_test01.xml b/tests/VectorDrawableTest/res/drawable/vector_test01.xml
index a9091ab..bad5a46 100644
--- a/tests/VectorDrawableTest/res/drawable/vector_test01.xml
+++ b/tests/VectorDrawableTest/res/drawable/vector_test01.xml
@@ -23,12 +23,10 @@
         android:viewportHeight="512"
         android:viewportWidth="512" />
 
-    <group>
-        <path
-            android:name="002b"
-            android:pathData="M100,200c0,-100 150,-100 150,0s150,100 150,0t-200,299"
-            android:stroke="#FF0000FF"
-            android:strokeWidth="4" />
-    </group>
+    <path
+        android:name="002b"
+        android:pathData="M100,200c0,-100 150,-100 150,0s150,100 150,0t-200,299"
+        android:stroke="#FF0000FF"
+        android:strokeWidth="4" />
 
 </vector>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/drawable/vector_test02.xml b/tests/VectorDrawableTest/res/drawable/vector_test02.xml
index ab58c06..c92b6f4 100644
--- a/tests/VectorDrawableTest/res/drawable/vector_test02.xml
+++ b/tests/VectorDrawableTest/res/drawable/vector_test02.xml
@@ -23,12 +23,10 @@
         android:viewportHeight="512"
         android:viewportWidth="512" />
 
-    <group>
-        <path
-            android:name="002b"
-            android:pathData="M100,200c0,-100 150,-100 150,0s150,100 150,0T-200,299"
-            android:stroke="#FF0000FF"
-            android:strokeWidth="4" />
-    </group>
+    <path
+        android:name="002b"
+        android:pathData="M100,200c0,-100 150,-100 150,0s150,100 150,0T-200,299"
+        android:stroke="#FF0000FF"
+        android:strokeWidth="4" />
 
 </vector>
\ No newline at end of file
diff --git a/tools/aapt/AaptAssets.cpp b/tools/aapt/AaptAssets.cpp
index e0dab78..12d5389 100644
--- a/tools/aapt/AaptAssets.cpp
+++ b/tools/aapt/AaptAssets.cpp
@@ -3,8 +3,10 @@
 //
 
 #include "AaptAssets.h"
-#include "ResourceFilter.h"
+#include "AaptConfig.h"
+#include "AaptUtil.h"
 #include "Main.h"
+#include "ResourceFilter.h"
 
 #include <utils/misc.h>
 #include <utils/SortedVector.h>
@@ -14,7 +16,6 @@
 #include <errno.h>
 
 static const char* kDefaultLocale = "default";
-static const char* kWildcardName = "any";
 static const char* kAssetDir = "assets";
 static const char* kResourceDir = "res";
 static const char* kValuesDir = "values";
@@ -149,24 +150,6 @@
 // =========================================================================
 // =========================================================================
 
-/* static */ void AaptLocaleValue::splitAndLowerCase(const char* const chars,
-        Vector<String8>* parts, const char separator) {
-    const char *p = chars;
-    const char *q;
-    while (NULL != (q = strchr(p, separator))) {
-         String8 val(p, q - p);
-         val.toLower();
-         parts->add(val);
-         p = q+1;
-    }
-
-    if (p < chars + strlen(chars)) {
-        String8 val(p);
-        val.toLower();
-        parts->add(val);
-    }
-}
-
 /* static */
 inline bool isAlpha(const String8& string) {
      const size_t length = string.length();
@@ -230,8 +213,7 @@
 bool AaptLocaleValue::initFromFilterString(const String8& str) {
      // A locale (as specified in the filter) is an underscore separated name such
      // as "en_US", "en_Latn_US", or "en_US_POSIX".
-     Vector<String8> parts;
-     splitAndLowerCase(str.string(), &parts, '_');
+     Vector<String8> parts = AaptUtil::splitAndLowerCase(str, '_');
 
      const int numTags = parts.size();
      bool valid = false;
@@ -301,8 +283,7 @@
     if (part[0] == 'b' && part[1] == '+') {
         // This is a "modified" BCP-47 language tag. Same semantics as BCP-47 tags,
         // except that the separator is "+" and not "-".
-        Vector<String8> subtags;
-        AaptLocaleValue::splitAndLowerCase(part.string(), &subtags, '+');
+        Vector<String8> subtags = AaptUtil::splitAndLowerCase(part, '+');
         subtags.removeItemsAt(0);
         if (subtags.size() == 1) {
             setLanguage(subtags[0]);
@@ -438,1349 +419,46 @@
     }
 }
 
-
-/* static */ bool
-AaptGroupEntry::parseFilterNamePart(const String8& part, int* axis, AxisValue* value)
-{
-    ResTable_config config;
-    memset(&config, 0, sizeof(ResTable_config));
-
-    // IMSI - MCC
-    if (getMccName(part.string(), &config)) {
-        *axis = AXIS_MCC;
-        value->intValue = config.mcc;
-        return true;
-    }
-
-    // IMSI - MNC
-    if (getMncName(part.string(), &config)) {
-        *axis = AXIS_MNC;
-        value->intValue = config.mnc;
-        return true;
-    }
-
-    // locale - language
-    if (value->localeValue.initFromFilterString(part)) {
-        *axis = AXIS_LOCALE;
-        return true;
-    }
-
-    // layout direction
-    if (getLayoutDirectionName(part.string(), &config)) {
-        *axis = AXIS_LAYOUTDIR;
-        value->intValue = (config.screenLayout&ResTable_config::MASK_LAYOUTDIR);
-        return true;
-    }
-
-    // smallest screen dp width
-    if (getSmallestScreenWidthDpName(part.string(), &config)) {
-        *axis = AXIS_SMALLESTSCREENWIDTHDP;
-        value->intValue = config.smallestScreenWidthDp;
-        return true;
-    }
-
-    // screen dp width
-    if (getScreenWidthDpName(part.string(), &config)) {
-        *axis = AXIS_SCREENWIDTHDP;
-        value->intValue = config.screenWidthDp;
-        return true;
-    }
-
-    // screen dp height
-    if (getScreenHeightDpName(part.string(), &config)) {
-        *axis = AXIS_SCREENHEIGHTDP;
-        value->intValue = config.screenHeightDp;
-        return true;
-    }
-
-    // screen layout size
-    if (getScreenLayoutSizeName(part.string(), &config)) {
-        *axis = AXIS_SCREENLAYOUTSIZE;
-        value->intValue = (config.screenLayout&ResTable_config::MASK_SCREENSIZE);
-        return true;
-    }
-
-    // screen layout long
-    if (getScreenLayoutLongName(part.string(), &config)) {
-        *axis = AXIS_SCREENLAYOUTLONG;
-        value->intValue = (config.screenLayout&ResTable_config::MASK_SCREENLONG);
-        return true;
-    }
-
-    // orientation
-    if (getOrientationName(part.string(), &config)) {
-        *axis = AXIS_ORIENTATION;
-        value->intValue = config.orientation;
-        return true;
-    }
-
-    // ui mode type
-    if (getUiModeTypeName(part.string(), &config)) {
-        *axis = AXIS_UIMODETYPE;
-        value->intValue = (config.uiMode&ResTable_config::MASK_UI_MODE_TYPE);
-        return true;
-    }
-
-    // ui mode night
-    if (getUiModeNightName(part.string(), &config)) {
-        *axis = AXIS_UIMODENIGHT;
-        value->intValue = (config.uiMode&ResTable_config::MASK_UI_MODE_NIGHT);
-        return true;
-    }
-
-    // density
-    if (getDensityName(part.string(), &config)) {
-        *axis = AXIS_DENSITY;
-        value->intValue = config.density;
-        return true;
-    }
-
-    // touchscreen
-    if (getTouchscreenName(part.string(), &config)) {
-        *axis = AXIS_TOUCHSCREEN;
-        value->intValue = config.touchscreen;
-        return true;
-    }
-
-    // keyboard hidden
-    if (getKeysHiddenName(part.string(), &config)) {
-        *axis = AXIS_KEYSHIDDEN;
-        value->intValue = config.inputFlags;
-        return true;
-    }
-
-    // keyboard
-    if (getKeyboardName(part.string(), &config)) {
-        *axis = AXIS_KEYBOARD;
-        value->intValue = config.keyboard;
-        return true;
-    }
-
-    // navigation hidden
-    if (getNavHiddenName(part.string(), &config)) {
-        *axis = AXIS_NAVHIDDEN;
-        value->intValue = config.inputFlags;
-        return 0;
-    }
-
-    // navigation
-    if (getNavigationName(part.string(), &config)) {
-        *axis = AXIS_NAVIGATION;
-        value->intValue = config.navigation;
-        return true;
-    }
-
-    // screen size
-    if (getScreenSizeName(part.string(), &config)) {
-        *axis = AXIS_SCREENSIZE;
-        value->intValue = config.screenSize;
-        return true;
-    }
-
-    // version
-    if (getVersionName(part.string(), &config)) {
-        *axis = AXIS_VERSION;
-        value->intValue = config.version;
-        return true;
-    }
-
-    return false;
-}
-
-AxisValue
-AaptGroupEntry::getConfigValueForAxis(const ResTable_config& config, int axis)
-{
-    AxisValue value;
-    switch (axis) {
-        case AXIS_MCC:
-            value.intValue = config.mcc;
-            break;
-        case AXIS_MNC:
-            value.intValue = config.mnc;
-            break;
-        case AXIS_LOCALE:
-            value.localeValue.initFromResTable(config);
-            break;
-        case AXIS_LAYOUTDIR:
-            value.intValue = config.screenLayout&ResTable_config::MASK_LAYOUTDIR;
-            break;
-        case AXIS_SCREENLAYOUTSIZE:
-            value.intValue = config.screenLayout&ResTable_config::MASK_SCREENSIZE;
-            break;
-        case AXIS_ORIENTATION:
-            value.intValue = config.orientation;
-            break;
-        case AXIS_UIMODETYPE:
-            value.intValue = (config.uiMode&ResTable_config::MASK_UI_MODE_TYPE);
-            break;
-        case AXIS_UIMODENIGHT:
-            value.intValue = (config.uiMode&ResTable_config::MASK_UI_MODE_NIGHT);
-            break;
-        case AXIS_DENSITY:
-            value.intValue = config.density;
-            break;
-        case AXIS_TOUCHSCREEN:
-            value.intValue = config.touchscreen;
-            break;
-        case AXIS_KEYSHIDDEN:
-            value.intValue = config.inputFlags;
-            break;
-        case AXIS_KEYBOARD:
-            value.intValue = config.keyboard;
-            break;
-        case AXIS_NAVIGATION:
-            value.intValue = config.navigation;
-            break;
-        case AXIS_SCREENSIZE:
-            value.intValue = config.screenSize;
-            break;
-        case AXIS_SMALLESTSCREENWIDTHDP:
-            value.intValue = config.smallestScreenWidthDp;
-            break;
-        case AXIS_SCREENWIDTHDP:
-            value.intValue = config.screenWidthDp;
-            break;
-        case AXIS_SCREENHEIGHTDP:
-            value.intValue = config.screenHeightDp;
-            break;
-        case AXIS_VERSION:
-            value.intValue = config.version;
-            break;
-    }
-
-    return value;
-}
-
-bool
-AaptGroupEntry::configSameExcept(const ResTable_config& config,
-        const ResTable_config& otherConfig, int axis)
-{
-    for (int i=AXIS_START; i<=AXIS_END; i++) {
-        if (i == axis) {
-            continue;
-        }
-        if (getConfigValueForAxis(config, i) != getConfigValueForAxis(otherConfig, i)) {
-            return false;
-        }
-    }
-    return true;
-}
-
 bool
 AaptGroupEntry::initFromDirName(const char* dir, String8* resType)
 {
-    mParamsChanged = true;
+    const char* q = strchr(dir, '-');
+    size_t typeLen;
+    if (q != NULL) {
+        typeLen = q - dir;
+    } else {
+        typeLen = strlen(dir);
+    }
 
-    Vector<String8> parts;
-    AaptLocaleValue::splitAndLowerCase(dir, &parts, '-');
-
-    String8 mcc, mnc, layoutsize, layoutlong, orient, den;
-    String8 touch, key, keysHidden, nav, navHidden, size, layoutDir, vers;
-    String8 uiModeType, uiModeNight, smallestwidthdp, widthdp, heightdp;
-
-    AaptLocaleValue locale;
-    int numLocaleComponents = 0;
-
-    const int N = parts.size();
-    int index = 0;
-    String8 part = parts[index];
-
-    // resource type
-    if (!isValidResourceType(part)) {
+    String8 type(dir, typeLen);
+    if (!isValidResourceType(type)) {
         return false;
     }
-    *resType = part;
 
-    index++;
-    if (index == N) {
-        goto success;
-    }
-    part = parts[index];
-
-    // imsi - mcc
-    if (getMccName(part.string())) {
-        mcc = part;
-
-        index++;
-        if (index == N) {
-            goto success;
+    if (q != NULL) {
+        if (!AaptConfig::parse(String8(q + 1), &mParams)) {
+            return false;
         }
-        part = parts[index];
-    } else {
-        //printf("not mcc: %s\n", part.string());
     }
 
-    // imsi - mnc
-    if (getMncName(part.string())) {
-        mnc = part;
-
-        index++;
-        if (index == N) {
-            goto success;
-        }
-        part = parts[index];
-    } else {
-        //printf("not mnc: %s\n", part.string());
-    }
-
-    index = locale.initFromDirName(parts, index);
-    if (index == -1) {
-        return false;
-    }
-    if (index >= N){
-        goto success;
-    }
-
-    part = parts[index];
-    if (getLayoutDirectionName(part.string())) {
-        layoutDir = part;
-
-        index++;
-        if (index == N) {
-            goto success;
-        }
-        part = parts[index];
-    } else {
-        //printf("not layout direction: %s\n", part.string());
-    }
-
-    if (getSmallestScreenWidthDpName(part.string())) {
-        smallestwidthdp = part;
-
-        index++;
-        if (index == N) {
-            goto success;
-        }
-        part = parts[index];
-    } else {
-        //printf("not smallest screen width dp: %s\n", part.string());
-    }
-
-    if (getScreenWidthDpName(part.string())) {
-        widthdp = part;
-
-        index++;
-        if (index == N) {
-            goto success;
-        }
-        part = parts[index];
-    } else {
-        //printf("not screen width dp: %s\n", part.string());
-    }
-
-    if (getScreenHeightDpName(part.string())) {
-        heightdp = part;
-
-        index++;
-        if (index == N) {
-            goto success;
-        }
-        part = parts[index];
-    } else {
-        //printf("not screen height dp: %s\n", part.string());
-    }
-
-    if (getScreenLayoutSizeName(part.string())) {
-        layoutsize = part;
-
-        index++;
-        if (index == N) {
-            goto success;
-        }
-        part = parts[index];
-    } else {
-        //printf("not screen layout size: %s\n", part.string());
-    }
-
-    if (getScreenLayoutLongName(part.string())) {
-        layoutlong = part;
-
-        index++;
-        if (index == N) {
-            goto success;
-        }
-        part = parts[index];
-    } else {
-        //printf("not screen layout long: %s\n", part.string());
-    }
-
-    // orientation
-    if (getOrientationName(part.string())) {
-        orient = part;
-
-        index++;
-        if (index == N) {
-            goto success;
-        }
-        part = parts[index];
-    } else {
-        //printf("not orientation: %s\n", part.string());
-    }
-
-    // ui mode type
-    if (getUiModeTypeName(part.string())) {
-        uiModeType = part;
-
-        index++;
-        if (index == N) {
-            goto success;
-        }
-        part = parts[index];
-    } else {
-        //printf("not ui mode type: %s\n", part.string());
-    }
-
-    // ui mode night
-    if (getUiModeNightName(part.string())) {
-        uiModeNight = part;
-
-        index++;
-        if (index == N) {
-            goto success;
-        }
-        part = parts[index];
-    } else {
-        //printf("not ui mode night: %s\n", part.string());
-    }
-
-    // density
-    if (getDensityName(part.string())) {
-        den = part;
-
-        index++;
-        if (index == N) {
-            goto success;
-        }
-        part = parts[index];
-    } else {
-        //printf("not density: %s\n", part.string());
-    }
-
-    // touchscreen
-    if (getTouchscreenName(part.string())) {
-        touch = part;
-
-        index++;
-        if (index == N) {
-            goto success;
-        }
-        part = parts[index];
-    } else {
-        //printf("not touchscreen: %s\n", part.string());
-    }
-
-    // keyboard hidden
-    if (getKeysHiddenName(part.string())) {
-        keysHidden = part;
-
-        index++;
-        if (index == N) {
-            goto success;
-        }
-        part = parts[index];
-    } else {
-        //printf("not keysHidden: %s\n", part.string());
-    }
-
-    // keyboard
-    if (getKeyboardName(part.string())) {
-        key = part;
-
-        index++;
-        if (index == N) {
-            goto success;
-        }
-        part = parts[index];
-    } else {
-        //printf("not keyboard: %s\n", part.string());
-    }
-
-    // navigation hidden
-    if (getNavHiddenName(part.string())) {
-        navHidden = part;
-
-        index++;
-        if (index == N) {
-            goto success;
-        }
-        part = parts[index];
-    } else {
-        //printf("not navHidden: %s\n", part.string());
-    }
-
-    if (getNavigationName(part.string())) {
-        nav = part;
-
-        index++;
-        if (index == N) {
-            goto success;
-        }
-        part = parts[index];
-    } else {
-        //printf("not navigation: %s\n", part.string());
-    }
-
-    if (getScreenSizeName(part.string())) {
-        size = part;
-
-        index++;
-        if (index == N) {
-            goto success;
-        }
-        part = parts[index];
-    } else {
-        //printf("not screen size: %s\n", part.string());
-    }
-
-    if (getVersionName(part.string())) {
-        vers = part;
-
-        index++;
-        if (index == N) {
-            goto success;
-        }
-        part = parts[index];
-    } else {
-        //printf("not version: %s\n", part.string());
-    }
-
-    // if there are extra parts, it doesn't match
-    return false;
-
-success:
-    this->mcc = mcc;
-    this->mnc = mnc;
-    this->locale = locale;
-    this->screenLayoutSize = layoutsize;
-    this->screenLayoutLong = layoutlong;
-    this->smallestScreenWidthDp = smallestwidthdp;
-    this->screenWidthDp = widthdp;
-    this->screenHeightDp = heightdp;
-    this->orientation = orient;
-    this->uiModeType = uiModeType;
-    this->uiModeNight = uiModeNight;
-    this->density = den;
-    this->touchscreen = touch;
-    this->keysHidden = keysHidden;
-    this->keyboard = key;
-    this->navHidden = navHidden;
-    this->navigation = nav;
-    this->screenSize = size;
-    this->layoutDirection = layoutDir;
-    this->version = vers;
-
-    // what is this anyway?
-    this->vendor = "";
-
+    *resType = type;
     return true;
 }
 
 String8
-AaptGroupEntry::toString() const
-{
-    String8 s = this->mcc;
-    s += ",";
-    s += this->mnc;
-    s += ",";
-    s += locale.toDirName();
-    s += ",";
-    s += layoutDirection;
-    s += ",";
-    s += smallestScreenWidthDp;
-    s += ",";
-    s += screenWidthDp;
-    s += ",";
-    s += screenHeightDp;
-    s += ",";
-    s += screenLayoutSize;
-    s += ",";
-    s += screenLayoutLong;
-    s += ",";
-    s += this->orientation;
-    s += ",";
-    s += uiModeType;
-    s += ",";
-    s += uiModeNight;
-    s += ",";
-    s += density;
-    s += ",";
-    s += touchscreen;
-    s += ",";
-    s += keysHidden;
-    s += ",";
-    s += keyboard;
-    s += ",";
-    s += navHidden;
-    s += ",";
-    s += navigation;
-    s += ",";
-    s += screenSize;
-    s += ",";
-    s += version;
-    return s;
-}
-
-String8
 AaptGroupEntry::toDirName(const String8& resType) const
 {
     String8 s = resType;
-    if (this->mcc != "") {
+    String8 params = mParams.toString();
+    if (params.length() > 0) {
         if (s.length() > 0) {
             s += "-";
         }
-        s += mcc;
+        s += params;
     }
-    if (this->mnc != "") {
-        if (s.length() > 0) {
-            s += "-";
-        }
-        s += mnc;
-    }
-
-    const String8 localeComponent = locale.toDirName();
-    if (localeComponent != "") {
-         if (s.length() > 0) {
-             s += "-";
-         }
-         s += localeComponent;
-    }
-
-    if (this->layoutDirection != "") {
-        if (s.length() > 0) {
-            s += "-";
-        }
-        s += layoutDirection;
-    }
-    if (this->smallestScreenWidthDp != "") {
-        if (s.length() > 0) {
-            s += "-";
-        }
-        s += smallestScreenWidthDp;
-    }
-    if (this->screenWidthDp != "") {
-        if (s.length() > 0) {
-            s += "-";
-        }
-        s += screenWidthDp;
-    }
-    if (this->screenHeightDp != "") {
-        if (s.length() > 0) {
-            s += "-";
-        }
-        s += screenHeightDp;
-    }
-    if (this->screenLayoutSize != "") {
-        if (s.length() > 0) {
-            s += "-";
-        }
-        s += screenLayoutSize;
-    }
-    if (this->screenLayoutLong != "") {
-        if (s.length() > 0) {
-            s += "-";
-        }
-        s += screenLayoutLong;
-    }
-    if (this->orientation != "") {
-        if (s.length() > 0) {
-            s += "-";
-        }
-        s += orientation;
-    }
-    if (this->uiModeType != "") {
-        if (s.length() > 0) {
-            s += "-";
-        }
-        s += uiModeType;
-    }
-    if (this->uiModeNight != "") {
-        if (s.length() > 0) {
-            s += "-";
-        }
-        s += uiModeNight;
-    }
-    if (this->density != "") {
-        if (s.length() > 0) {
-            s += "-";
-        }
-        s += density;
-    }
-    if (this->touchscreen != "") {
-        if (s.length() > 0) {
-            s += "-";
-        }
-        s += touchscreen;
-    }
-    if (this->keysHidden != "") {
-        if (s.length() > 0) {
-            s += "-";
-        }
-        s += keysHidden;
-    }
-    if (this->keyboard != "") {
-        if (s.length() > 0) {
-            s += "-";
-        }
-        s += keyboard;
-    }
-    if (this->navHidden != "") {
-        if (s.length() > 0) {
-            s += "-";
-        }
-        s += navHidden;
-    }
-    if (this->navigation != "") {
-        if (s.length() > 0) {
-            s += "-";
-        }
-        s += navigation;
-    }
-    if (this->screenSize != "") {
-        if (s.length() > 0) {
-            s += "-";
-        }
-        s += screenSize;
-    }
-    if (this->version != "") {
-        if (s.length() > 0) {
-            s += "-";
-        }
-        s += version;
-    }
-
     return s;
 }
 
-bool AaptGroupEntry::getMccName(const char* name,
-                                    ResTable_config* out)
-{
-    if (strcmp(name, kWildcardName) == 0) {
-        if (out) out->mcc = 0;
-        return true;
-    }
-    const char* c = name;
-    if (tolower(*c) != 'm') return false;
-    c++;
-    if (tolower(*c) != 'c') return false;
-    c++;
-    if (tolower(*c) != 'c') return false;
-    c++;
-
-    const char* val = c;
-
-    while (*c >= '0' && *c <= '9') {
-        c++;
-    }
-    if (*c != 0) return false;
-    if (c-val != 3) return false;
-
-    int d = atoi(val);
-    if (d != 0) {
-        if (out) out->mcc = d;
-        return true;
-    }
-
-    return false;
-}
-
-bool AaptGroupEntry::getMncName(const char* name,
-                                    ResTable_config* out)
-{
-    if (strcmp(name, kWildcardName) == 0) {
-        if (out) out->mcc = 0;
-        return true;
-    }
-    const char* c = name;
-    if (tolower(*c) != 'm') return false;
-    c++;
-    if (tolower(*c) != 'n') return false;
-    c++;
-    if (tolower(*c) != 'c') return false;
-    c++;
-
-    const char* val = c;
-
-    while (*c >= '0' && *c <= '9') {
-        c++;
-    }
-    if (*c != 0) return false;
-    if (c-val == 0 || c-val > 3) return false;
-
-    if (out) {
-        out->mnc = atoi(val);
-        if (out->mnc == 0) {
-            out->mnc = ACONFIGURATION_MNC_ZERO;
-        }
-    }
-
-    return true;
-}
-
-bool AaptGroupEntry::getLayoutDirectionName(const char* name, ResTable_config* out)
-{
-    if (strcmp(name, kWildcardName) == 0) {
-        if (out) out->screenLayout =
-                (out->screenLayout&~ResTable_config::MASK_LAYOUTDIR)
-                | ResTable_config::LAYOUTDIR_ANY;
-        return true;
-    } else if (strcmp(name, "ldltr") == 0) {
-        if (out) out->screenLayout =
-                (out->screenLayout&~ResTable_config::MASK_LAYOUTDIR)
-                | ResTable_config::LAYOUTDIR_LTR;
-        return true;
-    } else if (strcmp(name, "ldrtl") == 0) {
-        if (out) out->screenLayout =
-                (out->screenLayout&~ResTable_config::MASK_LAYOUTDIR)
-                | ResTable_config::LAYOUTDIR_RTL;
-        return true;
-    }
-
-    return false;
-}
-
-bool AaptGroupEntry::getScreenLayoutSizeName(const char* name,
-                                     ResTable_config* out)
-{
-    if (strcmp(name, kWildcardName) == 0) {
-        if (out) out->screenLayout =
-                (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
-                | ResTable_config::SCREENSIZE_ANY;
-        return true;
-    } else if (strcmp(name, "small") == 0) {
-        if (out) out->screenLayout =
-                (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
-                | ResTable_config::SCREENSIZE_SMALL;
-        return true;
-    } else if (strcmp(name, "normal") == 0) {
-        if (out) out->screenLayout =
-                (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
-                | ResTable_config::SCREENSIZE_NORMAL;
-        return true;
-    } else if (strcmp(name, "large") == 0) {
-        if (out) out->screenLayout =
-                (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
-                | ResTable_config::SCREENSIZE_LARGE;
-        return true;
-    } else if (strcmp(name, "xlarge") == 0) {
-        if (out) out->screenLayout =
-                (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
-                | ResTable_config::SCREENSIZE_XLARGE;
-        return true;
-    }
-
-    return false;
-}
-
-bool AaptGroupEntry::getScreenLayoutLongName(const char* name,
-                                     ResTable_config* out)
-{
-    if (strcmp(name, kWildcardName) == 0) {
-        if (out) out->screenLayout =
-                (out->screenLayout&~ResTable_config::MASK_SCREENLONG)
-                | ResTable_config::SCREENLONG_ANY;
-        return true;
-    } else if (strcmp(name, "long") == 0) {
-        if (out) out->screenLayout =
-                (out->screenLayout&~ResTable_config::MASK_SCREENLONG)
-                | ResTable_config::SCREENLONG_YES;
-        return true;
-    } else if (strcmp(name, "notlong") == 0) {
-        if (out) out->screenLayout =
-                (out->screenLayout&~ResTable_config::MASK_SCREENLONG)
-                | ResTable_config::SCREENLONG_NO;
-        return true;
-    }
-
-    return false;
-}
-
-bool AaptGroupEntry::getOrientationName(const char* name,
-                                        ResTable_config* out)
-{
-    if (strcmp(name, kWildcardName) == 0) {
-        if (out) out->orientation = out->ORIENTATION_ANY;
-        return true;
-    } else if (strcmp(name, "port") == 0) {
-        if (out) out->orientation = out->ORIENTATION_PORT;
-        return true;
-    } else if (strcmp(name, "land") == 0) {
-        if (out) out->orientation = out->ORIENTATION_LAND;
-        return true;
-    } else if (strcmp(name, "square") == 0) {
-        if (out) out->orientation = out->ORIENTATION_SQUARE;
-        return true;
-    }
-
-    return false;
-}
-
-bool AaptGroupEntry::getUiModeTypeName(const char* name,
-                                       ResTable_config* out)
-{
-    if (strcmp(name, kWildcardName) == 0) {
-        if (out) out->uiMode =
-                (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
-                | ResTable_config::UI_MODE_TYPE_ANY;
-        return true;
-    } else if (strcmp(name, "desk") == 0) {
-      if (out) out->uiMode =
-              (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
-              | ResTable_config::UI_MODE_TYPE_DESK;
-        return true;
-    } else if (strcmp(name, "car") == 0) {
-      if (out) out->uiMode =
-              (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
-              | ResTable_config::UI_MODE_TYPE_CAR;
-        return true;
-    } else if (strcmp(name, "television") == 0) {
-      if (out) out->uiMode =
-              (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
-              | ResTable_config::UI_MODE_TYPE_TELEVISION;
-        return true;
-    } else if (strcmp(name, "appliance") == 0) {
-      if (out) out->uiMode =
-              (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
-              | ResTable_config::UI_MODE_TYPE_APPLIANCE;
-        return true;
-    } else if (strcmp(name, "watch") == 0) {
-      if (out) out->uiMode =
-              (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
-              | ResTable_config::UI_MODE_TYPE_WATCH;
-        return true;
-    }
-
-    return false;
-}
-
-bool AaptGroupEntry::getUiModeNightName(const char* name,
-                                          ResTable_config* out)
-{
-    if (strcmp(name, kWildcardName) == 0) {
-        if (out) out->uiMode =
-                (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT)
-                | ResTable_config::UI_MODE_NIGHT_ANY;
-        return true;
-    } else if (strcmp(name, "night") == 0) {
-        if (out) out->uiMode =
-                (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT)
-                | ResTable_config::UI_MODE_NIGHT_YES;
-        return true;
-    } else if (strcmp(name, "notnight") == 0) {
-      if (out) out->uiMode =
-              (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT)
-              | ResTable_config::UI_MODE_NIGHT_NO;
-        return true;
-    }
-
-    return false;
-}
-
-bool AaptGroupEntry::getDensityName(const char* name,
-                                    ResTable_config* out)
-{
-    if (strcmp(name, kWildcardName) == 0) {
-        if (out) out->density = ResTable_config::DENSITY_DEFAULT;
-        return true;
-    }
-    
-    if (strcmp(name, "nodpi") == 0) {
-        if (out) out->density = ResTable_config::DENSITY_NONE;
-        return true;
-    }
-    
-    if (strcmp(name, "ldpi") == 0) {
-        if (out) out->density = ResTable_config::DENSITY_LOW;
-        return true;
-    }
-    
-    if (strcmp(name, "mdpi") == 0) {
-        if (out) out->density = ResTable_config::DENSITY_MEDIUM;
-        return true;
-    }
-    
-    if (strcmp(name, "tvdpi") == 0) {
-        if (out) out->density = ResTable_config::DENSITY_TV;
-        return true;
-    }
-    
-    if (strcmp(name, "hdpi") == 0) {
-        if (out) out->density = ResTable_config::DENSITY_HIGH;
-        return true;
-    }
-
-    if (strcmp(name, "xhdpi") == 0) {
-        if (out) out->density = ResTable_config::DENSITY_XHIGH;
-        return true;
-    }
-
-    if (strcmp(name, "xxhdpi") == 0) {
-        if (out) out->density = ResTable_config::DENSITY_XXHIGH;
-        return true;
-    }
-
-    if (strcmp(name, "xxxhdpi") == 0) {
-        if (out) out->density = ResTable_config::DENSITY_XXXHIGH;
-        return true;
-    }
-
-    char* c = (char*)name;
-    while (*c >= '0' && *c <= '9') {
-        c++;
-    }
-
-    // check that we have 'dpi' after the last digit.
-    if (toupper(c[0]) != 'D' ||
-            toupper(c[1]) != 'P' ||
-            toupper(c[2]) != 'I' ||
-            c[3] != 0) {
-        return false;
-    }
-
-    // temporarily replace the first letter with \0 to
-    // use atoi.
-    char tmp = c[0];
-    c[0] = '\0';
-
-    int d = atoi(name);
-    c[0] = tmp;
-
-    if (d != 0) {
-        if (out) out->density = d;
-        return true;
-    }
-
-    return false;
-}
-
-bool AaptGroupEntry::getTouchscreenName(const char* name,
-                                        ResTable_config* out)
-{
-    if (strcmp(name, kWildcardName) == 0) {
-        if (out) out->touchscreen = out->TOUCHSCREEN_ANY;
-        return true;
-    } else if (strcmp(name, "notouch") == 0) {
-        if (out) out->touchscreen = out->TOUCHSCREEN_NOTOUCH;
-        return true;
-    } else if (strcmp(name, "stylus") == 0) {
-        if (out) out->touchscreen = out->TOUCHSCREEN_STYLUS;
-        return true;
-    } else if (strcmp(name, "finger") == 0) {
-        if (out) out->touchscreen = out->TOUCHSCREEN_FINGER;
-        return true;
-    }
-
-    return false;
-}
-
-bool AaptGroupEntry::getKeysHiddenName(const char* name,
-                                       ResTable_config* out)
-{
-    uint8_t mask = 0;
-    uint8_t value = 0;
-    if (strcmp(name, kWildcardName) == 0) {
-        mask = ResTable_config::MASK_KEYSHIDDEN;
-        value = ResTable_config::KEYSHIDDEN_ANY;
-    } else if (strcmp(name, "keysexposed") == 0) {
-        mask = ResTable_config::MASK_KEYSHIDDEN;
-        value = ResTable_config::KEYSHIDDEN_NO;
-    } else if (strcmp(name, "keyshidden") == 0) {
-        mask = ResTable_config::MASK_KEYSHIDDEN;
-        value = ResTable_config::KEYSHIDDEN_YES;
-    } else if (strcmp(name, "keyssoft") == 0) {
-        mask = ResTable_config::MASK_KEYSHIDDEN;
-        value = ResTable_config::KEYSHIDDEN_SOFT;
-    }
-
-    if (mask != 0) {
-        if (out) out->inputFlags = (out->inputFlags&~mask) | value;
-        return true;
-    }
-
-    return false;
-}
-
-bool AaptGroupEntry::getKeyboardName(const char* name,
-                                        ResTable_config* out)
-{
-    if (strcmp(name, kWildcardName) == 0) {
-        if (out) out->keyboard = out->KEYBOARD_ANY;
-        return true;
-    } else if (strcmp(name, "nokeys") == 0) {
-        if (out) out->keyboard = out->KEYBOARD_NOKEYS;
-        return true;
-    } else if (strcmp(name, "qwerty") == 0) {
-        if (out) out->keyboard = out->KEYBOARD_QWERTY;
-        return true;
-    } else if (strcmp(name, "12key") == 0) {
-        if (out) out->keyboard = out->KEYBOARD_12KEY;
-        return true;
-    }
-
-    return false;
-}
-
-bool AaptGroupEntry::getNavHiddenName(const char* name,
-                                       ResTable_config* out)
-{
-    uint8_t mask = 0;
-    uint8_t value = 0;
-    if (strcmp(name, kWildcardName) == 0) {
-        mask = ResTable_config::MASK_NAVHIDDEN;
-        value = ResTable_config::NAVHIDDEN_ANY;
-    } else if (strcmp(name, "navexposed") == 0) {
-        mask = ResTable_config::MASK_NAVHIDDEN;
-        value = ResTable_config::NAVHIDDEN_NO;
-    } else if (strcmp(name, "navhidden") == 0) {
-        mask = ResTable_config::MASK_NAVHIDDEN;
-        value = ResTable_config::NAVHIDDEN_YES;
-    }
-
-    if (mask != 0) {
-        if (out) out->inputFlags = (out->inputFlags&~mask) | value;
-        return true;
-    }
-
-    return false;
-}
-
-bool AaptGroupEntry::getNavigationName(const char* name,
-                                     ResTable_config* out)
-{
-    if (strcmp(name, kWildcardName) == 0) {
-        if (out) out->navigation = out->NAVIGATION_ANY;
-        return true;
-    } else if (strcmp(name, "nonav") == 0) {
-        if (out) out->navigation = out->NAVIGATION_NONAV;
-        return true;
-    } else if (strcmp(name, "dpad") == 0) {
-        if (out) out->navigation = out->NAVIGATION_DPAD;
-        return true;
-    } else if (strcmp(name, "trackball") == 0) {
-        if (out) out->navigation = out->NAVIGATION_TRACKBALL;
-        return true;
-    } else if (strcmp(name, "wheel") == 0) {
-        if (out) out->navigation = out->NAVIGATION_WHEEL;
-        return true;
-    }
-
-    return false;
-}
-
-bool AaptGroupEntry::getScreenSizeName(const char* name, ResTable_config* out)
-{
-    if (strcmp(name, kWildcardName) == 0) {
-        if (out) {
-            out->screenWidth = out->SCREENWIDTH_ANY;
-            out->screenHeight = out->SCREENHEIGHT_ANY;
-        }
-        return true;
-    }
-
-    const char* x = name;
-    while (*x >= '0' && *x <= '9') x++;
-    if (x == name || *x != 'x') return false;
-    String8 xName(name, x-name);
-    x++;
-
-    const char* y = x;
-    while (*y >= '0' && *y <= '9') y++;
-    if (y == name || *y != 0) return false;
-    String8 yName(x, y-x);
-
-    uint16_t w = (uint16_t)atoi(xName.string());
-    uint16_t h = (uint16_t)atoi(yName.string());
-    if (w < h) {
-        return false;
-    }
-
-    if (out) {
-        out->screenWidth = w;
-        out->screenHeight = h;
-    }
-
-    return true;
-}
-
-bool AaptGroupEntry::getSmallestScreenWidthDpName(const char* name, ResTable_config* out)
-{
-    if (strcmp(name, kWildcardName) == 0) {
-        if (out) {
-            out->smallestScreenWidthDp = out->SCREENWIDTH_ANY;
-        }
-        return true;
-    }
-
-    if (*name != 's') return false;
-    name++;
-    if (*name != 'w') return false;
-    name++;
-    const char* x = name;
-    while (*x >= '0' && *x <= '9') x++;
-    if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false;
-    String8 xName(name, x-name);
-
-    if (out) {
-        out->smallestScreenWidthDp = (uint16_t)atoi(xName.string());
-    }
-
-    return true;
-}
-
-bool AaptGroupEntry::getScreenWidthDpName(const char* name, ResTable_config* out)
-{
-    if (strcmp(name, kWildcardName) == 0) {
-        if (out) {
-            out->screenWidthDp = out->SCREENWIDTH_ANY;
-        }
-        return true;
-    }
-
-    if (*name != 'w') return false;
-    name++;
-    const char* x = name;
-    while (*x >= '0' && *x <= '9') x++;
-    if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false;
-    String8 xName(name, x-name);
-
-    if (out) {
-        out->screenWidthDp = (uint16_t)atoi(xName.string());
-    }
-
-    return true;
-}
-
-bool AaptGroupEntry::getScreenHeightDpName(const char* name, ResTable_config* out)
-{
-    if (strcmp(name, kWildcardName) == 0) {
-        if (out) {
-            out->screenHeightDp = out->SCREENWIDTH_ANY;
-        }
-        return true;
-    }
-
-    if (*name != 'h') return false;
-    name++;
-    const char* x = name;
-    while (*x >= '0' && *x <= '9') x++;
-    if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false;
-    String8 xName(name, x-name);
-
-    if (out) {
-        out->screenHeightDp = (uint16_t)atoi(xName.string());
-    }
-
-    return true;
-}
-
-bool AaptGroupEntry::getVersionName(const char* name, ResTable_config* out)
-{
-    if (strcmp(name, kWildcardName) == 0) {
-        if (out) {
-            out->sdkVersion = out->SDKVERSION_ANY;
-            out->minorVersion = out->MINORVERSION_ANY;
-        }
-        return true;
-    }
-
-    if (*name != 'v') {
-        return false;
-    }
-
-    name++;
-    const char* s = name;
-    while (*s >= '0' && *s <= '9') s++;
-    if (s == name || *s != 0) return false;
-    String8 sdkName(name, s-name);
-
-    if (out) {
-        out->sdkVersion = (uint16_t)atoi(sdkName.string());
-        out->minorVersion = 0;
-    }
-
-    return true;
-}
-
-int AaptGroupEntry::compare(const AaptGroupEntry& o) const
-{
-    int v = mcc.compare(o.mcc);
-    if (v == 0) v = mnc.compare(o.mnc);
-    if (v == 0) v = locale.compare(o.locale);
-    if (v == 0) v = layoutDirection.compare(o.layoutDirection);
-    if (v == 0) v = vendor.compare(o.vendor);
-    if (v == 0) v = smallestScreenWidthDp.compare(o.smallestScreenWidthDp);
-    if (v == 0) v = screenWidthDp.compare(o.screenWidthDp);
-    if (v == 0) v = screenHeightDp.compare(o.screenHeightDp);
-    if (v == 0) v = screenLayoutSize.compare(o.screenLayoutSize);
-    if (v == 0) v = screenLayoutLong.compare(o.screenLayoutLong);
-    if (v == 0) v = orientation.compare(o.orientation);
-    if (v == 0) v = uiModeType.compare(o.uiModeType);
-    if (v == 0) v = uiModeNight.compare(o.uiModeNight);
-    if (v == 0) v = density.compare(o.density);
-    if (v == 0) v = touchscreen.compare(o.touchscreen);
-    if (v == 0) v = keysHidden.compare(o.keysHidden);
-    if (v == 0) v = keyboard.compare(o.keyboard);
-    if (v == 0) v = navHidden.compare(o.navHidden);
-    if (v == 0) v = navigation.compare(o.navigation);
-    if (v == 0) v = screenSize.compare(o.screenSize);
-    if (v == 0) v = version.compare(o.version);
-    return v;
-}
-
-const ResTable_config AaptGroupEntry::toParams() const
-{
-    if (!mParamsChanged) {
-        return mParams;
-    }
-
-    mParamsChanged = false;
-    ResTable_config& params = mParams;
-    memset(&params, 0, sizeof(ResTable_config));
-    getMccName(mcc.string(), &params);
-    getMncName(mnc.string(), &params);
-    locale.writeTo(&params);
-    getLayoutDirectionName(layoutDirection.string(), &params);
-    getSmallestScreenWidthDpName(smallestScreenWidthDp.string(), &params);
-    getScreenWidthDpName(screenWidthDp.string(), &params);
-    getScreenHeightDpName(screenHeightDp.string(), &params);
-    getScreenLayoutSizeName(screenLayoutSize.string(), &params);
-    getScreenLayoutLongName(screenLayoutLong.string(), &params);
-    getOrientationName(orientation.string(), &params);
-    getUiModeTypeName(uiModeType.string(), &params);
-    getUiModeNightName(uiModeNight.string(), &params);
-    getDensityName(density.string(), &params);
-    getTouchscreenName(touchscreen.string(), &params);
-    getKeysHiddenName(keysHidden.string(), &params);
-    getKeyboardName(keyboard.string(), &params);
-    getNavHiddenName(navHidden.string(), &params);
-    getNavigationName(navigation.string(), &params);
-    getScreenSizeName(screenSize.string(), &params);
-    getVersionName(version.string(), &params);
-    
-    // Fix up version number based on specified parameters.
-    int minSdk = 0;
-    if (params.smallestScreenWidthDp != ResTable_config::SCREENWIDTH_ANY
-            || params.screenWidthDp != ResTable_config::SCREENWIDTH_ANY
-            || params.screenHeightDp != ResTable_config::SCREENHEIGHT_ANY) {
-        minSdk = SDK_HONEYCOMB_MR2;
-    } else if ((params.uiMode&ResTable_config::MASK_UI_MODE_TYPE)
-                != ResTable_config::UI_MODE_TYPE_ANY
-            ||  (params.uiMode&ResTable_config::MASK_UI_MODE_NIGHT)
-                != ResTable_config::UI_MODE_NIGHT_ANY) {
-        minSdk = SDK_FROYO;
-    } else if ((params.screenLayout&ResTable_config::MASK_SCREENSIZE)
-                != ResTable_config::SCREENSIZE_ANY
-            ||  (params.screenLayout&ResTable_config::MASK_SCREENLONG)
-                != ResTable_config::SCREENLONG_ANY
-            || params.density != ResTable_config::DENSITY_DEFAULT) {
-        minSdk = SDK_DONUT;
-    }
-    
-    if (minSdk > params.sdkVersion) {
-        params.sdkVersion = minSdk;
-    }
-    
-    return params;
-}
 
 // =========================================================================
 // =========================================================================
@@ -2229,9 +907,7 @@
     : AaptDir(String8(), String8()),
       mHavePrivateSymbols(false),
       mChanged(false), mHaveIncludedAssets(false),
-      mRes(NULL)
-{
-}
+      mRes(NULL) {}
 
 const SortedVector<AaptGroupEntry>& AaptAssets::getGroupEntries() const {
     if (mChanged) {
@@ -2506,7 +1182,7 @@
         String8 resType;
         bool b = group.initFromDirName(entry->d_name, &resType);
         if (!b) {
-            fprintf(stderr, "invalid resource directory name: %s/%s\n", srcDir.string(),
+            fprintf(stderr, "invalid resource directory name: %s %s\n", srcDir.string(),
                     entry->d_name);
             err = -1;
             continue;
@@ -2654,30 +1330,35 @@
 
 status_t AaptAssets::filter(Bundle* bundle)
 {
-    ResourceFilter reqFilter;
+    WeakResourceFilter reqFilter;
     status_t err = reqFilter.parse(bundle->getConfigurations());
     if (err != NO_ERROR) {
         return err;
     }
 
-    ResourceFilter prefFilter;
-    err = prefFilter.parse(bundle->getPreferredConfigurations());
-    if (err != NO_ERROR) {
-        return err;
+    uint32_t preferredDensity = 0;
+    if (bundle->getPreferredDensity().size() > 0) {
+        ResTable_config preferredConfig;
+        if (!AaptConfig::parseDensity(bundle->getPreferredDensity().string(), &preferredConfig)) {
+            fprintf(stderr, "Error parsing preferred density: %s\n",
+                    bundle->getPreferredDensity().string());
+            return UNKNOWN_ERROR;
+        }
+        preferredDensity = preferredConfig.density;
     }
 
-    if (reqFilter.isEmpty() && prefFilter.isEmpty()) {
+    if (reqFilter.isEmpty() && preferredDensity == 0) {
         return NO_ERROR;
     }
 
     if (bundle->getVerbose()) {
         if (!reqFilter.isEmpty()) {
             printf("Applying required filter: %s\n",
-                    bundle->getConfigurations());
+                    bundle->getConfigurations().string());
         }
-        if (!prefFilter.isEmpty()) {
-            printf("Applying preferred filter: %s\n",
-                    bundle->getPreferredConfigurations());
+        if (preferredDensity > 0) {
+            printf("Applying preferred density filter: %s\n",
+                    bundle->getPreferredDensity().string());
         }
     }
 
@@ -2734,89 +1415,71 @@
             }
 
             // Quick check: no preferred filters, nothing more to do.
-            if (prefFilter.isEmpty()) {
+            if (preferredDensity == 0) {
                 continue;
             }
 
             // Get the preferred density if there is one. We do not match exactly for density.
             // If our preferred density is hdpi but we only have mdpi and xhdpi resources, we
             // pick xhdpi.
-            uint32_t preferredDensity = 0;
-            const SortedVector<AxisValue>* preferredConfigs = prefFilter.configsForAxis(AXIS_DENSITY);
-            if (preferredConfigs != NULL && preferredConfigs->size() > 0) {
-                preferredDensity = (*preferredConfigs)[0].intValue;
-            }
+            for (size_t k=0; k<grp->getFiles().size(); k++) {
+                sp<AaptFile> file = grp->getFiles().valueAt(k);
+                if (k == 0 && grp->getFiles().size() == 1) {
+                    // If this is the only file left, we need to keep it.
+                    // Otherwise the resource IDs we are using will be inconsistent
+                    // with what we get when not stripping.  Sucky, but at least
+                    // for now we can rely on the back-end doing another filtering
+                    // pass to take this out and leave us with this resource name
+                    // containing no entries.
+                    continue;
+                }
+                if (file->getPath().getPathExtension() == ".xml") {
+                    // We can't remove .xml files at this point, because when
+                    // we parse them they may add identifier resources, so
+                    // removing them can cause our resource identifiers to
+                    // become inconsistent.
+                    continue;
+                }
+                const ResTable_config& config(file->getGroupEntry().toParams());
+                if (config.density != 0 && config.density != preferredDensity) {
+                    // This is a resource we would prefer not to have.  Check
+                    // to see if have a similar variation that we would like
+                    // to have and, if so, we can drop it.
+                    uint32_t bestDensity = config.density;
 
-            // Now deal with preferred configurations.
-            for (int axis=AXIS_START; axis<=AXIS_END; axis++) {
-                for (size_t k=0; k<grp->getFiles().size(); k++) {
-                    sp<AaptFile> file = grp->getFiles().valueAt(k);
-                    if (k == 0 && grp->getFiles().size() == 1) {
-                        // If this is the only file left, we need to keep it.
-                        // Otherwise the resource IDs we are using will be inconsistent
-                        // with what we get when not stripping.  Sucky, but at least
-                        // for now we can rely on the back-end doing another filtering
-                        // pass to take this out and leave us with this resource name
-                        // containing no entries.
-                        continue;
-                    }
-                    if (file->getPath().getPathExtension() == ".xml") {
-                        // We can't remove .xml files at this point, because when
-                        // we parse them they may add identifier resources, so
-                        // removing them can cause our resource identifiers to
-                        // become inconsistent.
-                        continue;
-                    }
-                    const ResTable_config& config(file->getGroupEntry().toParams());
-                    if (!prefFilter.match(axis, config)) {
-                        // This is a resource we would prefer not to have.  Check
-                        // to see if have a similar variation that we would like
-                        // to have and, if so, we can drop it.
-
-                        uint32_t bestDensity = config.density;
-
-                        for (size_t m=0; m<grp->getFiles().size(); m++) {
-                            if (m == k) continue;
-                            sp<AaptFile> mfile = grp->getFiles().valueAt(m);
-                            const ResTable_config& mconfig(mfile->getGroupEntry().toParams());
-                            if (AaptGroupEntry::configSameExcept(config, mconfig, axis)) {
-                                if (axis == AXIS_DENSITY && preferredDensity > 0) {
-                                    // See if there is a better density resource
-                                    if (mconfig.density < bestDensity &&
-                                            mconfig.density > preferredDensity &&
-                                            bestDensity > preferredDensity) {
-                                        // This density is between our best density and
-                                        // the preferred density, therefore it is better.
-                                        bestDensity = mconfig.density;
-                                    } else if (mconfig.density > bestDensity &&
-                                            bestDensity < preferredDensity) {
-                                        // This density is better than our best density and
-                                        // our best density was smaller than our preferred
-                                        // density, so it is better.
-                                        bestDensity = mconfig.density;
-                                    }
-                                } else if (prefFilter.match(axis, mconfig)) {
-                                    if (bundle->getVerbose()) {
-                                        printf("Pruning unneeded resource: %s\n",
-                                                file->getPrintableSource().string());
-                                    }
-                                    grp->removeFile(k);
-                                    k--;
-                                    break;
-                                }
-                            }
+                    for (size_t m=0; m<grp->getFiles().size(); m++) {
+                        if (m == k) {
+                            continue;
                         }
 
-                        if (axis == AXIS_DENSITY && preferredDensity > 0 &&
-                                bestDensity != config.density) {
-                            if (bundle->getVerbose()) {
-                                printf("Pruning unneeded resource: %s\n",
-                                        file->getPrintableSource().string());
+                        sp<AaptFile> mfile = grp->getFiles().valueAt(m);
+                        const ResTable_config& mconfig(mfile->getGroupEntry().toParams());
+                        if (AaptConfig::isSameExcept(config, mconfig, ResTable_config::CONFIG_DENSITY)) {
+                            // See if there is a better density resource
+                            if (mconfig.density < bestDensity &&
+                                    mconfig.density > preferredDensity &&
+                                    bestDensity > preferredDensity) {
+                                // This density is between our best density and
+                                // the preferred density, therefore it is better.
+                                bestDensity = mconfig.density;
+                            } else if (mconfig.density > bestDensity &&
+                                    bestDensity < preferredDensity) {
+                                // This density is better than our best density and
+                                // our best density was smaller than our preferred
+                                // density, so it is better.
+                                bestDensity = mconfig.density;
                             }
-                            grp->removeFile(k);
-                            k--;
                         }
                     }
+
+                    if (bestDensity != config.density) {
+                        if (bundle->getVerbose()) {
+                            printf("Pruning unneeded resource: %s\n",
+                                    file->getPrintableSource().string());
+                        }
+                        grp->removeFile(k);
+                        k--;
+                    }
                 }
             }
         }
diff --git a/tools/aapt/AaptAssets.h b/tools/aapt/AaptAssets.h
index 82dda5f..0c2576a 100644
--- a/tools/aapt/AaptAssets.h
+++ b/tools/aapt/AaptAssets.h
@@ -6,22 +6,24 @@
 #ifndef __AAPT_ASSETS_H
 #define __AAPT_ASSETS_H
 
-#include <stdlib.h>
 #include <androidfw/AssetManager.h>
 #include <androidfw/ResourceTypes.h>
+#include <stdlib.h>
+#include <set>
 #include <utils/KeyedVector.h>
 #include <utils/RefBase.h>
 #include <utils/SortedVector.h>
 #include <utils/String8.h>
 #include <utils/Vector.h>
+
+#include "AaptConfig.h"
+#include "Bundle.h"
+#include "ConfigDescription.h"
+#include "SourcePos.h"
 #include "ZipFile.h"
 
-#include "Bundle.h"
-#include "SourcePos.h"
-
 using namespace android;
 
-
 extern const char * const gDefaultIgnoreAssets;
 extern const char * gUserIgnoreAssets;
 
@@ -82,9 +84,6 @@
          return memcmp(this, &other, sizeof(AaptLocaleValue));
      }
 
-     static void splitAndLowerCase(const char* const chars, Vector<String8>* parts,
-             const char separator);
-
      inline bool operator<(const AaptLocaleValue& o) const { return compare(o) < 0; }
      inline bool operator<=(const AaptLocaleValue& o) const { return compare(o) <= 0; }
      inline bool operator==(const AaptLocaleValue& o) const { return compare(o) == 0; }
@@ -98,31 +97,6 @@
      void setVariant(const char* variant);
 };
 
-struct AxisValue {
-    // Used for all axes except AXIS_LOCALE, which is represented
-    // as a AaptLocaleValue value.
-    int intValue;
-    AaptLocaleValue localeValue;
-
-    AxisValue() : intValue(0) {
-    }
-
-    inline int compare(const AxisValue &other) const  {
-        if (intValue != other.intValue) {
-            return intValue - other.intValue;
-        }
-
-        return localeValue.compare(other.localeValue);
-    }
-
-    inline bool operator<(const AxisValue& o) const { return compare(o) < 0; }
-    inline bool operator<=(const AxisValue& o) const { return compare(o) <= 0; }
-    inline bool operator==(const AxisValue& o) const { return compare(o) == 0; }
-    inline bool operator!=(const AxisValue& o) const { return compare(o) != 0; }
-    inline bool operator>=(const AxisValue& o) const { return compare(o) >= 0; }
-    inline bool operator>(const AxisValue& o) const { return compare(o) > 0; }
-};
-
 /**
  * This structure contains a specific variation of a single file out
  * of all the variations it can have that we can have.
@@ -130,23 +104,11 @@
 struct AaptGroupEntry
 {
 public:
-    AaptGroupEntry() : mParamsChanged(true) {
-        memset(&mParams, 0, sizeof(ResTable_config));
-    }
-
     bool initFromDirName(const char* dir, String8* resType);
 
-    static bool parseFilterNamePart(const String8& part, int* axis, AxisValue* value);
+    inline const ConfigDescription& toParams() const { return mParams; }
 
-    static AxisValue getConfigValueForAxis(const ResTable_config& config, int axis);
-
-    static bool configSameExcept(const ResTable_config& config,
-            const ResTable_config& otherConfig, int axis);
-
-    int compare(const AaptGroupEntry& o) const;
-
-    const ResTable_config toParams() const;
-
+    inline int compare(const AaptGroupEntry& o) const { return mParams.compareLogical(o.mParams); }
     inline bool operator<(const AaptGroupEntry& o) const { return compare(o) < 0; }
     inline bool operator<=(const AaptGroupEntry& o) const { return compare(o) <= 0; }
     inline bool operator==(const AaptGroupEntry& o) const { return compare(o) == 0; }
@@ -154,56 +116,13 @@
     inline bool operator>=(const AaptGroupEntry& o) const { return compare(o) >= 0; }
     inline bool operator>(const AaptGroupEntry& o) const { return compare(o) > 0; }
 
-    String8 toString() const;
+    String8 toString() const { return mParams.toString(); }
     String8 toDirName(const String8& resType) const;
 
-    const String8& getVersionString() const { return version; }
+    const String8 getVersionString() const { return AaptConfig::getVersion(mParams); }
 
 private:
-    static bool getMccName(const char* name, ResTable_config* out = NULL);
-    static bool getMncName(const char* name, ResTable_config* out = NULL);
-    static bool getScreenLayoutSizeName(const char* name, ResTable_config* out = NULL);
-    static bool getScreenLayoutLongName(const char* name, ResTable_config* out = NULL);
-    static bool getOrientationName(const char* name, ResTable_config* out = NULL);
-    static bool getUiModeTypeName(const char* name, ResTable_config* out = NULL);
-    static bool getUiModeNightName(const char* name, ResTable_config* out = NULL);
-    static bool getDensityName(const char* name, ResTable_config* out = NULL);
-    static bool getTouchscreenName(const char* name, ResTable_config* out = NULL);
-    static bool getKeysHiddenName(const char* name, ResTable_config* out = NULL);
-    static bool getKeyboardName(const char* name, ResTable_config* out = NULL);
-    static bool getNavigationName(const char* name, ResTable_config* out = NULL);
-    static bool getNavHiddenName(const char* name, ResTable_config* out = NULL);
-    static bool getScreenSizeName(const char* name, ResTable_config* out = NULL);
-    static bool getSmallestScreenWidthDpName(const char* name, ResTable_config* out = NULL);
-    static bool getScreenWidthDpName(const char* name, ResTable_config* out = NULL);
-    static bool getScreenHeightDpName(const char* name, ResTable_config* out = NULL);
-    static bool getLayoutDirectionName(const char* name, ResTable_config* out = NULL);
-    static bool getVersionName(const char* name, ResTable_config* out = NULL);
-
-    String8 mcc;
-    String8 mnc;
-    AaptLocaleValue locale;
-    String8 vendor;
-    String8 smallestScreenWidthDp;
-    String8 screenWidthDp;
-    String8 screenHeightDp;
-    String8 screenLayoutSize;
-    String8 screenLayoutLong;
-    String8 orientation;
-    String8 uiModeType;
-    String8 uiModeNight;
-    String8 density;
-    String8 touchscreen;
-    String8 keysHidden;
-    String8 keyboard;
-    String8 navHidden;
-    String8 navigation;
-    String8 screenSize;
-    String8 layoutDirection;
-    String8 version;
-
-    mutable bool mParamsChanged;
-    mutable ResTable_config mParams;
+    ConfigDescription mParams;
 };
 
 inline int compare_type(const AaptGroupEntry& lhs, const AaptGroupEntry& rhs)
diff --git a/tools/aapt/AaptConfig.cpp b/tools/aapt/AaptConfig.cpp
new file mode 100644
index 0000000..69a9c7f
--- /dev/null
+++ b/tools/aapt/AaptConfig.cpp
@@ -0,0 +1,790 @@
+/*
+ * 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.
+ */
+
+#include <androidfw/ResourceTypes.h>
+#include <ctype.h>
+
+#include "AaptConfig.h"
+#include "AaptAssets.h"
+#include "AaptUtil.h"
+#include "ResourceFilter.h"
+
+using android::String8;
+using android::Vector;
+using android::ResTable_config;
+
+namespace AaptConfig {
+
+static const char* kWildcardName = "any";
+
+bool parse(const String8& str, ConfigDescription* out) {
+    Vector<String8> parts = AaptUtil::splitAndLowerCase(str, '-');
+
+    ConfigDescription config;
+    AaptLocaleValue locale;
+    ssize_t index = 0;
+    ssize_t localeIndex = 0;
+    const ssize_t N = parts.size();
+    const char* part = parts[index].string();
+
+    if (str.length() == 0) {
+        goto success;
+    }
+
+    if (parseMcc(part, &config)) {
+        index++;
+        if (index == N) {
+            goto success;
+        }
+        part = parts[index].string();
+    }
+
+    if (parseMnc(part, &config)) {
+        index++;
+        if (index == N) {
+            goto success;
+        }
+        part = parts[index].string();
+    }
+
+    // Locale spans a few '-' separators, so we let it
+    // control the index.
+    localeIndex = locale.initFromDirName(parts, index);
+    if (localeIndex < 0) {
+        return false;
+    } else if (localeIndex > index) {
+        locale.writeTo(&config);
+        index = localeIndex;
+        if (index >= N) {
+            goto success;
+        }
+        part = parts[index].string();
+    }
+
+    if (parseLayoutDirection(part, &config)) {
+        index++;
+        if (index == N) {
+            goto success;
+        }
+        part = parts[index].string();
+    }
+
+    if (parseSmallestScreenWidthDp(part, &config)) {
+        index++;
+        if (index == N) {
+            goto success;
+        }
+        part = parts[index].string();
+    }
+
+    if (parseScreenWidthDp(part, &config)) {
+        index++;
+        if (index == N) {
+            goto success;
+        }
+        part = parts[index].string();
+    }
+
+    if (parseScreenHeightDp(part, &config)) {
+        index++;
+        if (index == N) {
+            goto success;
+        }
+        part = parts[index].string();
+    }
+
+    if (parseScreenLayoutSize(part, &config)) {
+        index++;
+        if (index == N) {
+            goto success;
+        }
+        part = parts[index].string();
+    }
+
+    if (parseScreenLayoutLong(part, &config)) {
+        index++;
+        if (index == N) {
+            goto success;
+        }
+        part = parts[index].string();
+    }
+
+    if (parseOrientation(part, &config)) {
+        index++;
+        if (index == N) {
+            goto success;
+        }
+        part = parts[index].string();
+    }
+
+    if (parseUiModeType(part, &config)) {
+        index++;
+        if (index == N) {
+            goto success;
+        }
+        part = parts[index].string();
+    }
+
+    if (parseUiModeNight(part, &config)) {
+        index++;
+        if (index == N) {
+            goto success;
+        }
+        part = parts[index].string();
+    }
+
+    if (parseDensity(part, &config)) {
+        index++;
+        if (index == N) {
+            goto success;
+        }
+        part = parts[index].string();
+    }
+
+    if (parseTouchscreen(part, &config)) {
+        index++;
+        if (index == N) {
+            goto success;
+        }
+        part = parts[index].string();
+    }
+
+    if (parseKeysHidden(part, &config)) {
+        index++;
+        if (index == N) {
+            goto success;
+        }
+        part = parts[index].string();
+    }
+
+    if (parseKeyboard(part, &config)) {
+        index++;
+        if (index == N) {
+            goto success;
+        }
+        part = parts[index].string();
+    }
+
+    if (parseNavHidden(part, &config)) {
+        index++;
+        if (index == N) {
+            goto success;
+        }
+        part = parts[index].string();
+    }
+
+    if (parseNavigation(part, &config)) {
+        index++;
+        if (index == N) {
+            goto success;
+        }
+        part = parts[index].string();
+    }
+
+    if (parseScreenSize(part, &config)) {
+        index++;
+        if (index == N) {
+            goto success;
+        }
+        part = parts[index].string();
+    }
+
+    if (parseVersion(part, &config)) {
+        index++;
+        if (index == N) {
+            goto success;
+        }
+        part = parts[index].string();
+    }
+
+    // Unrecognized.
+    return false;
+
+success:
+    if (out != NULL) {
+        applyVersionForCompatibility(&config);
+        *out = config;
+    }
+    return true;
+}
+
+bool parseCommaSeparatedList(const String8& str, std::set<ConfigDescription>* outSet) {
+    Vector<String8> parts = AaptUtil::splitAndLowerCase(str, ',');
+    const size_t N = parts.size();
+    for (size_t i = 0; i < N; i++) {
+        ConfigDescription config;
+        if (!parse(parts[i], &config)) {
+            return false;
+        }
+        outSet->insert(config);
+    }
+    return true;
+}
+
+void applyVersionForCompatibility(ConfigDescription* config) {
+    if (config == NULL) {
+        return;
+    }
+
+    uint16_t minSdk = 0;
+    if (config->smallestScreenWidthDp != ResTable_config::SCREENWIDTH_ANY
+            || config->screenWidthDp != ResTable_config::SCREENWIDTH_ANY
+            || config->screenHeightDp != ResTable_config::SCREENHEIGHT_ANY) {
+        minSdk = SDK_HONEYCOMB_MR2;
+    } else if ((config->uiMode & ResTable_config::MASK_UI_MODE_TYPE)
+                != ResTable_config::UI_MODE_TYPE_ANY
+            ||  (config->uiMode & ResTable_config::MASK_UI_MODE_NIGHT)
+                != ResTable_config::UI_MODE_NIGHT_ANY) {
+        minSdk = SDK_FROYO;
+    } else if ((config->screenLayout & ResTable_config::MASK_SCREENSIZE)
+                != ResTable_config::SCREENSIZE_ANY
+            ||  (config->screenLayout & ResTable_config::MASK_SCREENLONG)
+                != ResTable_config::SCREENLONG_ANY
+            || config->density != ResTable_config::DENSITY_DEFAULT) {
+        minSdk = SDK_DONUT;
+    }
+
+    if (minSdk > config->sdkVersion) {
+        config->sdkVersion = minSdk;
+    }
+}
+
+bool parseMcc(const char* name, ResTable_config* out) {
+    if (strcmp(name, kWildcardName) == 0) {
+        if (out) out->mcc = 0;
+        return true;
+    }
+    const char* c = name;
+    if (tolower(*c) != 'm') return false;
+    c++;
+    if (tolower(*c) != 'c') return false;
+    c++;
+    if (tolower(*c) != 'c') return false;
+    c++;
+
+    const char* val = c;
+
+    while (*c >= '0' && *c <= '9') {
+        c++;
+    }
+    if (*c != 0) return false;
+    if (c-val != 3) return false;
+
+    int d = atoi(val);
+    if (d != 0) {
+        if (out) out->mcc = d;
+        return true;
+    }
+
+    return false;
+}
+
+bool parseMnc(const char* name, ResTable_config* out) {
+    if (strcmp(name, kWildcardName) == 0) {
+        if (out) out->mcc = 0;
+        return true;
+    }
+    const char* c = name;
+    if (tolower(*c) != 'm') return false;
+    c++;
+    if (tolower(*c) != 'n') return false;
+    c++;
+    if (tolower(*c) != 'c') return false;
+    c++;
+
+    const char* val = c;
+
+    while (*c >= '0' && *c <= '9') {
+        c++;
+    }
+    if (*c != 0) return false;
+    if (c-val == 0 || c-val > 3) return false;
+
+    if (out) {
+        out->mnc = atoi(val);
+        if (out->mnc == 0) {
+            out->mnc = ACONFIGURATION_MNC_ZERO;
+        }
+    }
+
+    return true;
+}
+
+bool parseLayoutDirection(const char* name, ResTable_config* out) {
+    if (strcmp(name, kWildcardName) == 0) {
+        if (out) out->screenLayout =
+                (out->screenLayout&~ResTable_config::MASK_LAYOUTDIR)
+                | ResTable_config::LAYOUTDIR_ANY;
+        return true;
+    } else if (strcmp(name, "ldltr") == 0) {
+        if (out) out->screenLayout =
+                (out->screenLayout&~ResTable_config::MASK_LAYOUTDIR)
+                | ResTable_config::LAYOUTDIR_LTR;
+        return true;
+    } else if (strcmp(name, "ldrtl") == 0) {
+        if (out) out->screenLayout =
+                (out->screenLayout&~ResTable_config::MASK_LAYOUTDIR)
+                | ResTable_config::LAYOUTDIR_RTL;
+        return true;
+    }
+
+    return false;
+}
+
+bool parseScreenLayoutSize(const char* name, ResTable_config* out) {
+    if (strcmp(name, kWildcardName) == 0) {
+        if (out) out->screenLayout =
+                (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
+                | ResTable_config::SCREENSIZE_ANY;
+        return true;
+    } else if (strcmp(name, "small") == 0) {
+        if (out) out->screenLayout =
+                (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
+                | ResTable_config::SCREENSIZE_SMALL;
+        return true;
+    } else if (strcmp(name, "normal") == 0) {
+        if (out) out->screenLayout =
+                (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
+                | ResTable_config::SCREENSIZE_NORMAL;
+        return true;
+    } else if (strcmp(name, "large") == 0) {
+        if (out) out->screenLayout =
+                (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
+                | ResTable_config::SCREENSIZE_LARGE;
+        return true;
+    } else if (strcmp(name, "xlarge") == 0) {
+        if (out) out->screenLayout =
+                (out->screenLayout&~ResTable_config::MASK_SCREENSIZE)
+                | ResTable_config::SCREENSIZE_XLARGE;
+        return true;
+    }
+
+    return false;
+}
+
+bool parseScreenLayoutLong(const char* name, ResTable_config* out) {
+    if (strcmp(name, kWildcardName) == 0) {
+        if (out) out->screenLayout =
+                (out->screenLayout&~ResTable_config::MASK_SCREENLONG)
+                | ResTable_config::SCREENLONG_ANY;
+        return true;
+    } else if (strcmp(name, "long") == 0) {
+        if (out) out->screenLayout =
+                (out->screenLayout&~ResTable_config::MASK_SCREENLONG)
+                | ResTable_config::SCREENLONG_YES;
+        return true;
+    } else if (strcmp(name, "notlong") == 0) {
+        if (out) out->screenLayout =
+                (out->screenLayout&~ResTable_config::MASK_SCREENLONG)
+                | ResTable_config::SCREENLONG_NO;
+        return true;
+    }
+
+    return false;
+}
+
+bool parseOrientation(const char* name, ResTable_config* out) {
+    if (strcmp(name, kWildcardName) == 0) {
+        if (out) out->orientation = out->ORIENTATION_ANY;
+        return true;
+    } else if (strcmp(name, "port") == 0) {
+        if (out) out->orientation = out->ORIENTATION_PORT;
+        return true;
+    } else if (strcmp(name, "land") == 0) {
+        if (out) out->orientation = out->ORIENTATION_LAND;
+        return true;
+    } else if (strcmp(name, "square") == 0) {
+        if (out) out->orientation = out->ORIENTATION_SQUARE;
+        return true;
+    }
+
+    return false;
+}
+
+bool parseUiModeType(const char* name, ResTable_config* out) {
+    if (strcmp(name, kWildcardName) == 0) {
+        if (out) out->uiMode =
+                (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
+                | ResTable_config::UI_MODE_TYPE_ANY;
+        return true;
+    } else if (strcmp(name, "desk") == 0) {
+      if (out) out->uiMode =
+              (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
+              | ResTable_config::UI_MODE_TYPE_DESK;
+        return true;
+    } else if (strcmp(name, "car") == 0) {
+      if (out) out->uiMode =
+              (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
+              | ResTable_config::UI_MODE_TYPE_CAR;
+        return true;
+    } else if (strcmp(name, "television") == 0) {
+      if (out) out->uiMode =
+              (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
+              | ResTable_config::UI_MODE_TYPE_TELEVISION;
+        return true;
+    } else if (strcmp(name, "appliance") == 0) {
+      if (out) out->uiMode =
+              (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
+              | ResTable_config::UI_MODE_TYPE_APPLIANCE;
+        return true;
+    } else if (strcmp(name, "watch") == 0) {
+      if (out) out->uiMode =
+              (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
+              | ResTable_config::UI_MODE_TYPE_WATCH;
+        return true;
+    }
+
+    return false;
+}
+
+bool parseUiModeNight(const char* name, ResTable_config* out) {
+    if (strcmp(name, kWildcardName) == 0) {
+        if (out) out->uiMode =
+                (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT)
+                | ResTable_config::UI_MODE_NIGHT_ANY;
+        return true;
+    } else if (strcmp(name, "night") == 0) {
+        if (out) out->uiMode =
+                (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT)
+                | ResTable_config::UI_MODE_NIGHT_YES;
+        return true;
+    } else if (strcmp(name, "notnight") == 0) {
+      if (out) out->uiMode =
+              (out->uiMode&~ResTable_config::MASK_UI_MODE_NIGHT)
+              | ResTable_config::UI_MODE_NIGHT_NO;
+        return true;
+    }
+
+    return false;
+}
+
+bool parseDensity(const char* name, ResTable_config* out) {
+    if (strcmp(name, kWildcardName) == 0) {
+        if (out) out->density = ResTable_config::DENSITY_DEFAULT;
+        return true;
+    }
+
+    if (strcmp(name, "nodpi") == 0) {
+        if (out) out->density = ResTable_config::DENSITY_NONE;
+        return true;
+    }
+
+    if (strcmp(name, "ldpi") == 0) {
+        if (out) out->density = ResTable_config::DENSITY_LOW;
+        return true;
+    }
+
+    if (strcmp(name, "mdpi") == 0) {
+        if (out) out->density = ResTable_config::DENSITY_MEDIUM;
+        return true;
+    }
+
+    if (strcmp(name, "tvdpi") == 0) {
+        if (out) out->density = ResTable_config::DENSITY_TV;
+        return true;
+    }
+
+    if (strcmp(name, "hdpi") == 0) {
+        if (out) out->density = ResTable_config::DENSITY_HIGH;
+        return true;
+    }
+
+    if (strcmp(name, "xhdpi") == 0) {
+        if (out) out->density = ResTable_config::DENSITY_XHIGH;
+        return true;
+    }
+
+    if (strcmp(name, "xxhdpi") == 0) {
+        if (out) out->density = ResTable_config::DENSITY_XXHIGH;
+        return true;
+    }
+
+    if (strcmp(name, "xxxhdpi") == 0) {
+        if (out) out->density = ResTable_config::DENSITY_XXXHIGH;
+        return true;
+    }
+
+    char* c = (char*)name;
+    while (*c >= '0' && *c <= '9') {
+        c++;
+    }
+
+    // check that we have 'dpi' after the last digit.
+    if (toupper(c[0]) != 'D' ||
+            toupper(c[1]) != 'P' ||
+            toupper(c[2]) != 'I' ||
+            c[3] != 0) {
+        return false;
+    }
+
+    // temporarily replace the first letter with \0 to
+    // use atoi.
+    char tmp = c[0];
+    c[0] = '\0';
+
+    int d = atoi(name);
+    c[0] = tmp;
+
+    if (d != 0) {
+        if (out) out->density = d;
+        return true;
+    }
+
+    return false;
+}
+
+bool parseTouchscreen(const char* name, ResTable_config* out) {
+    if (strcmp(name, kWildcardName) == 0) {
+        if (out) out->touchscreen = out->TOUCHSCREEN_ANY;
+        return true;
+    } else if (strcmp(name, "notouch") == 0) {
+        if (out) out->touchscreen = out->TOUCHSCREEN_NOTOUCH;
+        return true;
+    } else if (strcmp(name, "stylus") == 0) {
+        if (out) out->touchscreen = out->TOUCHSCREEN_STYLUS;
+        return true;
+    } else if (strcmp(name, "finger") == 0) {
+        if (out) out->touchscreen = out->TOUCHSCREEN_FINGER;
+        return true;
+    }
+
+    return false;
+}
+
+bool parseKeysHidden(const char* name, ResTable_config* out) {
+    uint8_t mask = 0;
+    uint8_t value = 0;
+    if (strcmp(name, kWildcardName) == 0) {
+        mask = ResTable_config::MASK_KEYSHIDDEN;
+        value = ResTable_config::KEYSHIDDEN_ANY;
+    } else if (strcmp(name, "keysexposed") == 0) {
+        mask = ResTable_config::MASK_KEYSHIDDEN;
+        value = ResTable_config::KEYSHIDDEN_NO;
+    } else if (strcmp(name, "keyshidden") == 0) {
+        mask = ResTable_config::MASK_KEYSHIDDEN;
+        value = ResTable_config::KEYSHIDDEN_YES;
+    } else if (strcmp(name, "keyssoft") == 0) {
+        mask = ResTable_config::MASK_KEYSHIDDEN;
+        value = ResTable_config::KEYSHIDDEN_SOFT;
+    }
+
+    if (mask != 0) {
+        if (out) out->inputFlags = (out->inputFlags&~mask) | value;
+        return true;
+    }
+
+    return false;
+}
+
+bool parseKeyboard(const char* name, ResTable_config* out) {
+    if (strcmp(name, kWildcardName) == 0) {
+        if (out) out->keyboard = out->KEYBOARD_ANY;
+        return true;
+    } else if (strcmp(name, "nokeys") == 0) {
+        if (out) out->keyboard = out->KEYBOARD_NOKEYS;
+        return true;
+    } else if (strcmp(name, "qwerty") == 0) {
+        if (out) out->keyboard = out->KEYBOARD_QWERTY;
+        return true;
+    } else if (strcmp(name, "12key") == 0) {
+        if (out) out->keyboard = out->KEYBOARD_12KEY;
+        return true;
+    }
+
+    return false;
+}
+
+bool parseNavHidden(const char* name, ResTable_config* out) {
+    uint8_t mask = 0;
+    uint8_t value = 0;
+    if (strcmp(name, kWildcardName) == 0) {
+        mask = ResTable_config::MASK_NAVHIDDEN;
+        value = ResTable_config::NAVHIDDEN_ANY;
+    } else if (strcmp(name, "navexposed") == 0) {
+        mask = ResTable_config::MASK_NAVHIDDEN;
+        value = ResTable_config::NAVHIDDEN_NO;
+    } else if (strcmp(name, "navhidden") == 0) {
+        mask = ResTable_config::MASK_NAVHIDDEN;
+        value = ResTable_config::NAVHIDDEN_YES;
+    }
+
+    if (mask != 0) {
+        if (out) out->inputFlags = (out->inputFlags&~mask) | value;
+        return true;
+    }
+
+    return false;
+}
+
+bool parseNavigation(const char* name, ResTable_config* out) {
+    if (strcmp(name, kWildcardName) == 0) {
+        if (out) out->navigation = out->NAVIGATION_ANY;
+        return true;
+    } else if (strcmp(name, "nonav") == 0) {
+        if (out) out->navigation = out->NAVIGATION_NONAV;
+        return true;
+    } else if (strcmp(name, "dpad") == 0) {
+        if (out) out->navigation = out->NAVIGATION_DPAD;
+        return true;
+    } else if (strcmp(name, "trackball") == 0) {
+        if (out) out->navigation = out->NAVIGATION_TRACKBALL;
+        return true;
+    } else if (strcmp(name, "wheel") == 0) {
+        if (out) out->navigation = out->NAVIGATION_WHEEL;
+        return true;
+    }
+
+    return false;
+}
+
+bool parseScreenSize(const char* name, ResTable_config* out) {
+    if (strcmp(name, kWildcardName) == 0) {
+        if (out) {
+            out->screenWidth = out->SCREENWIDTH_ANY;
+            out->screenHeight = out->SCREENHEIGHT_ANY;
+        }
+        return true;
+    }
+
+    const char* x = name;
+    while (*x >= '0' && *x <= '9') x++;
+    if (x == name || *x != 'x') return false;
+    String8 xName(name, x-name);
+    x++;
+
+    const char* y = x;
+    while (*y >= '0' && *y <= '9') y++;
+    if (y == name || *y != 0) return false;
+    String8 yName(x, y-x);
+
+    uint16_t w = (uint16_t)atoi(xName.string());
+    uint16_t h = (uint16_t)atoi(yName.string());
+    if (w < h) {
+        return false;
+    }
+
+    if (out) {
+        out->screenWidth = w;
+        out->screenHeight = h;
+    }
+
+    return true;
+}
+
+bool parseSmallestScreenWidthDp(const char* name, ResTable_config* out) {
+    if (strcmp(name, kWildcardName) == 0) {
+        if (out) {
+            out->smallestScreenWidthDp = out->SCREENWIDTH_ANY;
+        }
+        return true;
+    }
+
+    if (*name != 's') return false;
+    name++;
+    if (*name != 'w') return false;
+    name++;
+    const char* x = name;
+    while (*x >= '0' && *x <= '9') x++;
+    if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false;
+    String8 xName(name, x-name);
+
+    if (out) {
+        out->smallestScreenWidthDp = (uint16_t)atoi(xName.string());
+    }
+
+    return true;
+}
+
+bool parseScreenWidthDp(const char* name, ResTable_config* out) {
+    if (strcmp(name, kWildcardName) == 0) {
+        if (out) {
+            out->screenWidthDp = out->SCREENWIDTH_ANY;
+        }
+        return true;
+    }
+
+    if (*name != 'w') return false;
+    name++;
+    const char* x = name;
+    while (*x >= '0' && *x <= '9') x++;
+    if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false;
+    String8 xName(name, x-name);
+
+    if (out) {
+        out->screenWidthDp = (uint16_t)atoi(xName.string());
+    }
+
+    return true;
+}
+
+bool parseScreenHeightDp(const char* name, ResTable_config* out) {
+    if (strcmp(name, kWildcardName) == 0) {
+        if (out) {
+            out->screenHeightDp = out->SCREENWIDTH_ANY;
+        }
+        return true;
+    }
+
+    if (*name != 'h') return false;
+    name++;
+    const char* x = name;
+    while (*x >= '0' && *x <= '9') x++;
+    if (x == name || x[0] != 'd' || x[1] != 'p' || x[2] != 0) return false;
+    String8 xName(name, x-name);
+
+    if (out) {
+        out->screenHeightDp = (uint16_t)atoi(xName.string());
+    }
+
+    return true;
+}
+
+bool parseVersion(const char* name, ResTable_config* out) {
+    if (strcmp(name, kWildcardName) == 0) {
+        if (out) {
+            out->sdkVersion = out->SDKVERSION_ANY;
+            out->minorVersion = out->MINORVERSION_ANY;
+        }
+        return true;
+    }
+
+    if (*name != 'v') {
+        return false;
+    }
+
+    name++;
+    const char* s = name;
+    while (*s >= '0' && *s <= '9') s++;
+    if (s == name || *s != 0) return false;
+    String8 sdkName(name, s-name);
+
+    if (out) {
+        out->sdkVersion = (uint16_t)atoi(sdkName.string());
+        out->minorVersion = 0;
+    }
+
+    return true;
+}
+
+String8 getVersion(const ResTable_config& config) {
+    return String8::format("v%u", config.sdkVersion);
+}
+
+bool isSameExcept(const ResTable_config& a, const ResTable_config& b, int axisMask) {
+    return a.diff(b) == axisMask;
+}
+
+} // namespace AaptConfig
diff --git a/tools/aapt/AaptConfig.h b/tools/aapt/AaptConfig.h
new file mode 100644
index 0000000..2963539
--- /dev/null
+++ b/tools/aapt/AaptConfig.h
@@ -0,0 +1,85 @@
+/*
+ * 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.
+ */
+
+#ifndef __AAPT_CONFIG_H
+#define __AAPT_CONFIG_H
+
+#include <set>
+#include <utils/String8.h>
+
+#include "ConfigDescription.h"
+
+/**
+ * Utility methods for dealing with configurations.
+ */
+namespace AaptConfig {
+
+/**
+ * Parse a string of the form 'fr-sw600dp-land' and fill in the
+ * given ResTable_config with resulting configuration parameters.
+ *
+ * The resulting configuration has the appropriate sdkVersion defined
+ * for backwards compatibility.
+ */
+bool parse(const android::String8& str, ConfigDescription* out = NULL);
+
+/**
+ * Parse a comma separated list of configuration strings. Duplicate configurations
+ * will be removed.
+ *
+ * Example input: "fr,de-land,fr-sw600dp-land"
+ */
+bool parseCommaSeparatedList(const android::String8& str, std::set<ConfigDescription>* outSet);
+
+/**
+ * If the configuration uses an axis that was added after
+ * the original Android release, make sure the SDK version
+ * is set accordingly.
+ */
+void applyVersionForCompatibility(ConfigDescription* config);
+
+// Individual axis
+bool parseMcc(const char* str, android::ResTable_config* out = NULL);
+bool parseMnc(const char* str, android::ResTable_config* out = NULL);
+bool parseLayoutDirection(const char* str, android::ResTable_config* out = NULL);
+bool parseSmallestScreenWidthDp(const char* str, android::ResTable_config* out = NULL);
+bool parseScreenWidthDp(const char* str, android::ResTable_config* out = NULL);
+bool parseScreenHeightDp(const char* str, android::ResTable_config* out = NULL);
+bool parseScreenLayoutSize(const char* str, android::ResTable_config* out = NULL);
+bool parseScreenLayoutLong(const char* str, android::ResTable_config* out = NULL);
+bool parseOrientation(const char* str, android::ResTable_config* out = NULL);
+bool parseUiModeType(const char* str, android::ResTable_config* out = NULL);
+bool parseUiModeNight(const char* str, android::ResTable_config* out = NULL);
+bool parseDensity(const char* str, android::ResTable_config* out = NULL);
+bool parseTouchscreen(const char* str, android::ResTable_config* out = NULL);
+bool parseKeysHidden(const char* str, android::ResTable_config* out = NULL);
+bool parseKeyboard(const char* str, android::ResTable_config* out = NULL);
+bool parseNavHidden(const char* str, android::ResTable_config* out = NULL);
+bool parseNavigation(const char* str, android::ResTable_config* out = NULL);
+bool parseScreenSize(const char* str, android::ResTable_config* out = NULL);
+bool parseVersion(const char* str, android::ResTable_config* out = NULL);
+
+android::String8 getVersion(const android::ResTable_config& config);
+
+/**
+ * Returns true if the two configurations only differ by the specified axis.
+ * The axis mask is a bitmask of CONFIG_* constants.
+ */
+bool isSameExcept(const android::ResTable_config& a, const android::ResTable_config& b, int configMask);
+
+} // namespace AaptConfig
+
+#endif // __AAPT_CONFIG_H
diff --git a/tools/aapt/AaptUtil.cpp b/tools/aapt/AaptUtil.cpp
new file mode 100644
index 0000000..293e144
--- /dev/null
+++ b/tools/aapt/AaptUtil.cpp
@@ -0,0 +1,64 @@
+/*
+ * 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.
+ */
+
+#include "AaptUtil.h"
+
+using android::Vector;
+using android::String8;
+
+namespace AaptUtil {
+
+Vector<String8> split(const String8& str, const char sep) {
+    Vector<String8> parts;
+    const char* p = str.string();
+    const char* q;
+
+    while (true) {
+        q = strchr(p, sep);
+        if (q == NULL) {
+            parts.add(String8(p, strlen(p)));
+            return parts;
+        }
+
+        parts.add(String8(p, q-p));
+        p = q + 1;
+    }
+    return parts;
+}
+
+Vector<String8> splitAndLowerCase(const String8& str, const char sep) {
+    Vector<String8> parts;
+    const char* p = str.string();
+    const char* q;
+
+    while (true) {
+        q = strchr(p, sep);
+        if (q == NULL) {
+            String8 val(p, strlen(p));
+            val.toLower();
+            parts.add(val);
+            return parts;
+        }
+
+        String8 val(p, q-p);
+        val.toLower();
+        parts.add(val);
+        p = q + 1;
+    }
+    return parts;
+}
+
+} // namespace AaptUtil
diff --git a/tools/aapt/AaptUtil.h b/tools/aapt/AaptUtil.h
new file mode 100644
index 0000000..47a704a
--- /dev/null
+++ b/tools/aapt/AaptUtil.h
@@ -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.
+ */
+
+#ifndef __AAPT_UTIL_H
+#define __AAPT_UTIL_H
+
+#include <utils/String8.h>
+#include <utils/Vector.h>
+
+namespace AaptUtil {
+
+android::Vector<android::String8> split(const android::String8& str, const char sep);
+android::Vector<android::String8> splitAndLowerCase(const android::String8& str, const char sep);
+
+} // namespace AaptUtil
+
+#endif // __AAPT_UTIL_H
diff --git a/tools/aapt/Android.mk b/tools/aapt/Android.mk
index 806f8ff..700afa1 100644
--- a/tools/aapt/Android.mk
+++ b/tools/aapt/Android.mk
@@ -1,104 +1,168 @@
-# 
-# Copyright 2006 The Android Open Source Project
 #
-# Android Asset Packaging Tool
+# 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.
 #
 
 # This tool is prebuilt if we're doing an app-only build.
 ifeq ($(TARGET_BUILD_APPS)$(filter true,$(TARGET_BUILD_PDK)),)
 
+# ==========================================================
+# Setup some common variables for the different build
+# targets here.
+# ==========================================================
+LOCAL_PATH:= $(call my-dir)
 
-aapt_src_files := \
-	AaptAssets.cpp \
-	Command.cpp \
-	CrunchCache.cpp \
-	FileFinder.cpp \
-	Main.cpp \
-	Package.cpp \
-	StringPool.cpp \
-	XMLNode.cpp \
-	ResourceFilter.cpp \
-	ResourceIdCache.cpp \
-	ResourceTable.cpp \
-	Images.cpp \
-	Resource.cpp \
+aaptMain := Main.cpp
+aaptSources := \
+    AaptAssets.cpp \
+    AaptConfig.cpp \
+    AaptUtil.cpp \
+    ApkBuilder.cpp \
+    Command.cpp \
+    CrunchCache.cpp \
+    FileFinder.cpp \
+    Package.cpp \
+    StringPool.cpp \
+    XMLNode.cpp \
+    ResourceFilter.cpp \
+    ResourceIdCache.cpp \
+    ResourceTable.cpp \
+    Images.cpp \
+    Resource.cpp \
     pseudolocalize.cpp \
     SourcePos.cpp \
-	WorkQueue.cpp \
+    WorkQueue.cpp \
     ZipEntry.cpp \
     ZipFile.cpp \
-	qsort_r_compat.c
+    qsort_r_compat.c
 
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
+aaptTests := \
+    tests/AaptConfig_test.cpp \
+    tests/AaptGroupEntry_test.cpp \
+    tests/ResourceFilter_test.cpp
 
-LOCAL_SRC_FILES := $(aapt_src_files)
+aaptCIncludes := \
+    external/libpng \
+    external/zlib
 
-LOCAL_CFLAGS += -Wno-format-y2k
-ifeq (darwin,$(HOST_OS))
-LOCAL_CFLAGS += -D_DARWIN_UNLIMITED_STREAMS
-endif
-
-LOCAL_CFLAGS += -DSTATIC_ANDROIDFW_FOR_TOOLS
-
-LOCAL_C_INCLUDES += external/libpng
-LOCAL_C_INCLUDES += external/zlib
-
-LOCAL_STATIC_LIBRARIES := \
-	libandroidfw \
-	libutils \
-	libcutils \
-	libexpat \
-	libpng \
-	liblog \
-	libziparchive-host
+aaptHostLdLibs :=
+aaptHostStaticLibs := \
+    libandroidfw \
+    libpng \
+    liblog \
+    libutils \
+    libcutils \
+    libexpat \
+    libziparchive-host
 
 ifeq ($(HOST_OS),linux)
-LOCAL_LDLIBS += -lrt -ldl -lpthread
+    aaptHostLdLibs += -lrt -ldl -lpthread
 endif
 
 # Statically link libz for MinGW (Win SDK under Linux),
 # and dynamically link for all others.
 ifneq ($(strip $(USE_MINGW)),)
-  LOCAL_STATIC_LIBRARIES += libz
+    aaptHostStaticLibs += libz
 else
-  LOCAL_LDLIBS += -lz
+    aaptHostLdLibs += -lz
 endif
 
+
+# ==========================================================
+# Build the host static library: libaapt
+# ==========================================================
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libaapt
+
+LOCAL_SRC_FILES := $(aaptSources)
+LOCAL_C_INCLUDES += $(aaptCIncludes)
+
+LOCAL_CFLAGS += -Wno-format-y2k
+LOCAL_CFLAGS += -DSTATIC_ANDROIDFW_FOR_TOOLS
+ifeq (darwin,$(HOST_OS))
+LOCAL_CFLAGS += -D_DARWIN_UNLIMITED_STREAMS
+endif
+
+include $(BUILD_HOST_STATIC_LIBRARY)
+
+
+# ==========================================================
+# Build the host executable: aapt
+# ==========================================================
+include $(CLEAR_VARS)
+
 LOCAL_MODULE := aapt
 
+LOCAL_SRC_FILES := $(aaptMain)
+
+LOCAL_STATIC_LIBRARIES += \
+    libaapt \
+    $(aaptHostStaticLibs)
+LOCAL_LDLIBS += $(aaptHostLdLibs)
+
 include $(BUILD_HOST_EXECUTABLE)
 
-# aapt for running on the device
-# =========================================================
+
+# ==========================================================
+# Build the host tests: libaapt_tests
+# ==========================================================
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libaapt_tests
+
+LOCAL_SRC_FILES += $(aaptTests)
+LOCAL_C_INCLUDES += $(LOCAL_PATH)
+
+LOCAL_STATIC_LIBRARIES += \
+    libaapt \
+    $(aaptHostStaticLibs)
+LOCAL_LDLIBS += $(aaptHostLdLibs)
+
+include $(BUILD_HOST_NATIVE_TEST)
+
+
+# ==========================================================
+# Build the device executable: aapt
+# ==========================================================
 ifneq ($(SDK_ONLY),true)
 include $(CLEAR_VARS)
 
-LOCAL_SRC_FILES := $(aapt_src_files)
-
 LOCAL_MODULE := aapt
 
-LOCAL_C_INCLUDES += bionic
-LOCAL_C_INCLUDES += bionic/libstdc++/include
-LOCAL_C_INCLUDES += external/stlport/stlport
-LOCAL_C_INCLUDES += external/libpng
-LOCAL_C_INCLUDES += external/zlib
-
-LOCAL_CFLAGS += -Wno-non-virtual-dtor
+LOCAL_SRC_FILES := $(aaptSources) $(aaptMain)
+LOCAL_C_INCLUDES += \
+    $(aaptCIncludes) \
+    bionic \
+    external/stlport/stlport
 
 LOCAL_SHARED_LIBRARIES := \
-        libandroidfw \
-        libutils \
-        libcutils \
-        libpng \
-        liblog \
-        libz
+    libandroidfw \
+    libutils \
+    libcutils \
+    libpng \
+    liblog \
+    libz
 
 LOCAL_STATIC_LIBRARIES := \
-        libstlport_static \
-        libexpat_static
+    libstlport_static \
+    libexpat_static
+
+LOCAL_CPPFLAGS += -Wno-non-virtual-dtor
 
 include $(BUILD_EXECUTABLE)
-endif
+
+endif # Not SDK_ONLY
 
 endif # No TARGET_BUILD_APPS or TARGET_BUILD_PDK
diff --git a/tools/aapt/ApkBuilder.cpp b/tools/aapt/ApkBuilder.cpp
new file mode 100644
index 0000000..12f6040
--- /dev/null
+++ b/tools/aapt/ApkBuilder.cpp
@@ -0,0 +1,111 @@
+/*
+ * 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.
+ */
+
+#include "AaptAssets.h"
+#include "ApkBuilder.h"
+
+using namespace android;
+
+ApkBuilder::ApkBuilder(const sp<WeakResourceFilter>& configFilter)
+    : mConfigFilter(configFilter)
+    , mDefaultFilter(new AndResourceFilter()) {
+    // Add the default split, which is present for all APKs.
+    mDefaultFilter->addFilter(mConfigFilter);
+    mSplits.add(new ApkSplit(std::set<ConfigDescription>(), mDefaultFilter, true));
+}
+
+status_t ApkBuilder::createSplitForConfigs(const std::set<ConfigDescription>& configs) {
+    const size_t N = mSplits.size();
+    for (size_t i = 0; i < N; i++) {
+        const std::set<ConfigDescription>& splitConfigs = mSplits[i]->getConfigs();
+        std::set<ConfigDescription>::const_iterator iter = configs.begin();
+        for (; iter != configs.end(); iter++) {
+            if (splitConfigs.count(*iter) > 0) {
+                // Can't have overlapping configurations.
+                fprintf(stderr, "ERROR: Split configuration '%s' is already defined "
+                        "in another split.\n", iter->toString().string());
+                return ALREADY_EXISTS;
+            }
+        }
+    }
+
+    sp<StrongResourceFilter> splitFilter = new StrongResourceFilter(configs);
+
+    // Add the inverse filter of this split filter to the base apk filter so it will
+    // omit resources that belong in this split.
+    mDefaultFilter->addFilter(new InverseResourceFilter(splitFilter));
+
+    // Now add the apk-wide config filter to our split filter.
+    sp<AndResourceFilter> filter = new AndResourceFilter();
+    filter->addFilter(splitFilter);
+    filter->addFilter(mConfigFilter);
+    mSplits.add(new ApkSplit(configs, filter));
+    return NO_ERROR;
+}
+
+status_t ApkBuilder::addEntry(const String8& path, const sp<AaptFile>& file) {
+    const size_t N = mSplits.size();
+    for (size_t i = 0; i < N; i++) {
+        if (mSplits[i]->matches(file)) {
+            return mSplits.editItemAt(i)->addEntry(path, file);
+        }
+    }
+    // Entry can be dropped if it doesn't match any split. This will only happen
+    // if the enry doesn't mConfigFilter.
+    return NO_ERROR;
+}
+
+void ApkBuilder::print() const {
+    fprintf(stderr, "APK Builder\n");
+    fprintf(stderr, "-----------\n");
+    const size_t N = mSplits.size();
+    for (size_t i = 0; i < N; i++) {
+        mSplits[i]->print();
+        fprintf(stderr, "\n");
+    }
+}
+
+ApkSplit::ApkSplit(const std::set<ConfigDescription>& configs, const sp<ResourceFilter>& filter, bool isBase)
+    : mConfigs(configs), mFilter(filter), mIsBase(isBase) {
+    std::set<ConfigDescription>::const_iterator iter = configs.begin();
+    for (; iter != configs.end(); iter++) {
+        if (mName.size() > 0) {
+            mName.append(",");
+            mDirName.append("_");
+        }
+
+        String8 configStr = iter->toString();
+        mName.append(configStr);
+        mDirName.append(configStr);
+    }
+}
+
+status_t ApkSplit::addEntry(const String8& path, const sp<AaptFile>& file) {
+    if (!mFiles.insert(OutputEntry(path, file)).second) {
+        // Duplicate file.
+        return ALREADY_EXISTS;
+    }
+    return NO_ERROR;
+}
+
+void ApkSplit::print() const {
+    fprintf(stderr, "APK Split '%s'\n", mName.string());
+
+    std::set<OutputEntry>::const_iterator iter = mFiles.begin();
+    for (; iter != mFiles.end(); iter++) {
+        fprintf(stderr, "  %s (%s)\n", iter->getPath().string(), iter->getFile()->getSourceFile().string());
+    }
+}
diff --git a/tools/aapt/ApkBuilder.h b/tools/aapt/ApkBuilder.h
new file mode 100644
index 0000000..a4b7d4a
--- /dev/null
+++ b/tools/aapt/ApkBuilder.h
@@ -0,0 +1,113 @@
+/*
+ * 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.
+ */
+
+#ifndef __APK_BUILDER_H
+#define __APK_BUILDER_H
+
+#include <set>
+#include <utils/Errors.h>
+#include <utils/String8.h>
+#include <utils/StrongPointer.h>
+#include <utils/Vector.h>
+
+#include "ConfigDescription.h"
+#include "OutputSet.h"
+#include "ResourceFilter.h"
+
+class ApkSplit;
+class AaptFile;
+
+class ApkBuilder : public android::RefBase {
+public:
+    ApkBuilder(const sp<WeakResourceFilter>& configFilter);
+
+    /**
+     * Tells the builder to generate a separate APK for resources that
+     * match the configurations specified. Split APKs can not have
+     * overlapping resources.
+     *
+     * NOTE: All splits should be set up before any files are added.
+     */
+    android::status_t createSplitForConfigs(const std::set<ConfigDescription>& configs);
+
+    /**
+     * Adds a file to be written to the final APK. It's name must not collide
+     * with that of any files previously added. When a Split APK is being
+     * generated, duplicates can exist as long as they are in different splits
+     * (resources.arsc, AndroidManifest.xml).
+     */
+    android::status_t addEntry(const String8& path, const android::sp<AaptFile>& file);
+
+    android::Vector<sp<ApkSplit> >& getSplits() {
+        return mSplits;
+    }
+
+    void print() const;
+
+private:
+    android::sp<ResourceFilter> mConfigFilter;
+    android::sp<AndResourceFilter> mDefaultFilter;
+    android::Vector<sp<ApkSplit> > mSplits;
+};
+
+class ApkSplit : public OutputSet {
+public:
+    android::status_t addEntry(const String8& path, const android::sp<AaptFile>& file);
+
+    const std::set<OutputEntry>& getEntries() const {
+        return mFiles;
+    }
+
+    const std::set<ConfigDescription>& getConfigs() const {
+        return mConfigs;
+    }
+
+    bool matches(const sp<AaptFile>& file) const {
+        return mFilter->match(file->getGroupEntry().toParams());
+    }
+
+    sp<ResourceFilter> getResourceFilter() const {
+        return mFilter;
+    }
+
+    const android::String8& getPrintableName() const {
+        return mName;
+    }
+
+    const android::String8& getDirectorySafeName() const {
+        return mDirName;
+    }
+
+    bool isBase() const {
+        return mIsBase;
+    }
+
+    void print() const;
+
+private:
+    friend class ApkBuilder;
+
+    ApkSplit(const std::set<ConfigDescription>& configs, const android::sp<ResourceFilter>& filter, bool isBase=false);
+
+    std::set<ConfigDescription> mConfigs;
+    const sp<ResourceFilter> mFilter;
+    const bool mIsBase;
+    String8 mName;
+    String8 mDirName;
+    std::set<OutputEntry> mFiles;
+};
+
+#endif // __APK_BUILDER_H
diff --git a/tools/aapt/Bundle.h b/tools/aapt/Bundle.h
index ebe1bed..ceb52a0 100644
--- a/tools/aapt/Bundle.h
+++ b/tools/aapt/Bundle.h
@@ -151,10 +151,12 @@
     void setPublicOutputFile(const char* file) { mPublicOutputFile = file; }
     const char* getRClassDir() const { return mRClassDir; }
     void setRClassDir(const char* dir) { mRClassDir = dir; }
-    const char* getConfigurations() const { return mConfigurations.size() > 0 ? mConfigurations.string() : NULL; }
+    const android::String8& getConfigurations() const { return mConfigurations; }
     void addConfigurations(const char* val) { if (mConfigurations.size() > 0) { mConfigurations.append(","); mConfigurations.append(val); } else { mConfigurations = val; } }
-    const char* getPreferredConfigurations() const { return mPreferredConfigurations.size() > 0 ? mPreferredConfigurations.string() : NULL; }
-    void addPreferredConfigurations(const char* val) { if (mPreferredConfigurations.size() > 0) { mPreferredConfigurations.append(","); mPreferredConfigurations.append(val); } else { mPreferredConfigurations = val; } }
+    const android::String8& getPreferredDensity() const { return mPreferredDensity; }
+    void setPreferredDensity(const char* val) { mPreferredDensity = val; }
+    void addSplitConfigurations(const char* val) { mPartialConfigurations.add(android::String8(val)); }
+    const android::Vector<android::String8>& getSplitConfigurations() const { return mPartialConfigurations; }
     const char* getResourceIntermediatesDir() const { return mResourceIntermediatesDir; }
     void setResourceIntermediatesDir(const char* dir) { mResourceIntermediatesDir = dir; }
     const android::Vector<const char*>& getPackageIncludes() const { return mPackageIncludes; }
@@ -286,7 +288,8 @@
     const char* mRClassDir;
     const char* mResourceIntermediatesDir;
     android::String8 mConfigurations;
-    android::String8 mPreferredConfigurations;
+    android::String8 mPreferredDensity;
+    android::Vector<android::String8> mPartialConfigurations;
     android::Vector<const char*> mPackageIncludes;
     android::Vector<const char*> mJarFiles;
     android::Vector<const char*> mNoCompressExtensions;
diff --git a/tools/aapt/Command.cpp b/tools/aapt/Command.cpp
index 0af1ce1..0360200 100644
--- a/tools/aapt/Command.cpp
+++ b/tools/aapt/Command.cpp
@@ -3,6 +3,7 @@
 //
 // Android Asset Packaging Tool main entry point.
 //
+#include "ApkBuilder.h"
 #include "Main.h"
 #include "Bundle.h"
 #include "ResourceFilter.h"
@@ -2034,6 +2035,47 @@
     return (result != NO_ERROR);
 }
 
+static status_t addResourcesToBuilder(const sp<AaptDir>& dir, const sp<ApkBuilder>& builder) {
+    const size_t numDirs = dir->getDirs().size();
+    for (size_t i = 0; i < numDirs; i++) {
+        status_t err = addResourcesToBuilder(dir->getDirs().valueAt(i), builder);
+        if (err != NO_ERROR) {
+            return err;
+        }
+    }
+
+    const size_t numFiles = dir->getFiles().size();
+    for (size_t i = 0; i < numFiles; i++) {
+        sp<AaptGroup> gp = dir->getFiles().valueAt(i);
+        const size_t numConfigs = gp->getFiles().size();
+        for (size_t j = 0; j < numConfigs; j++) {
+            status_t err = builder->addEntry(gp->getPath(), gp->getFiles().valueAt(j));
+            if (err != NO_ERROR) {
+                fprintf(stderr, "Failed to add %s (%s) to builder.\n",
+                        gp->getPath().string(), gp->getFiles()[j]->getPrintableSource().string());
+                return err;
+            }
+        }
+    }
+    return NO_ERROR;
+}
+
+static String8 buildApkName(const String8& original, const sp<ApkSplit>& split) {
+    if (split->isBase()) {
+        return original;
+    }
+
+    String8 ext(original.getPathExtension());
+    if (ext == String8(".apk")) {
+        return String8::format("%s_%s%s",
+                original.getBasePath().string(),
+                split->getDirectorySafeName().string(),
+                ext.string());
+    }
+
+    return String8::format("%s_%s", original.string(),
+            split->getDirectorySafeName().string());
+}
 
 /*
  * Package up an asset directory and associated application files.
@@ -2047,17 +2089,18 @@
     int N;
     FILE* fp;
     String8 dependencyFile;
+    sp<ApkBuilder> builder;
 
     // -c en_XA or/and ar_XB means do pseudolocalization
-    ResourceFilter filter;
-    err = filter.parse(bundle->getConfigurations());
+    sp<WeakResourceFilter> configFilter = new WeakResourceFilter();
+    err = configFilter->parse(bundle->getConfigurations());
     if (err != NO_ERROR) {
         goto bail;
     }
-    if (filter.containsPseudo()) {
+    if (configFilter->containsPseudo()) {
         bundle->setPseudolocalize(bundle->getPseudolocalize() | PSEUDO_ACCENTED);
     }
-    if (filter.containsPseudoBidi()) {
+    if (configFilter->containsPseudoBidi()) {
         bundle->setPseudolocalize(bundle->getPseudolocalize() | PSEUDO_BIDI);
     }
 
@@ -2105,9 +2148,32 @@
         assets->print(String8());
     }
 
+    // Create the ApkBuilder, which will collect the compiled files
+    // to write to the final APK (or sets of APKs if we are building
+    // a Split APK.
+    builder = new ApkBuilder(configFilter);
+
+    // If we are generating a Split APK, find out which configurations to split on.
+    if (bundle->getSplitConfigurations().size() > 0) {
+        const Vector<String8>& splitStrs = bundle->getSplitConfigurations();
+        const size_t numSplits = splitStrs.size();
+        for (size_t i = 0; i < numSplits; i++) {
+            std::set<ConfigDescription> configs;
+            if (!AaptConfig::parseCommaSeparatedList(splitStrs[i], &configs)) {
+                fprintf(stderr, "ERROR: failed to parse split configuration '%s'\n", splitStrs[i].string());
+                goto bail;
+            }
+
+            err = builder->createSplitForConfigs(configs);
+            if (err != NO_ERROR) {
+                goto bail;
+            }
+        }
+    }
+
     // If they asked for any fileAs that need to be compiled, do so.
     if (bundle->getResourceSourceDirs().size() || bundle->getAndroidManifestFile()) {
-        err = buildResources(bundle, assets);
+        err = buildResources(bundle, assets, builder);
         if (err != 0) {
             goto bail;
         }
@@ -2194,11 +2260,24 @@
 
     // Write the apk
     if (outputAPKFile) {
-        err = writeAPK(bundle, assets, String8(outputAPKFile));
+        // Gather all resources and add them to the APK Builder. The builder will then
+        // figure out which Split they belong in.
+        err = addResourcesToBuilder(assets, builder);
         if (err != NO_ERROR) {
-            fprintf(stderr, "ERROR: packaging of '%s' failed\n", outputAPKFile);
             goto bail;
         }
+
+        const Vector<sp<ApkSplit> >& splits = builder->getSplits();
+        const size_t numSplits = splits.size();
+        for (size_t i = 0; i < numSplits; i++) {
+            const sp<ApkSplit>& split = splits[i];
+            String8 outputPath = buildApkName(String8(outputAPKFile), split);
+            err = writeAPK(bundle, outputPath, split);
+            if (err != NO_ERROR) {
+                fprintf(stderr, "ERROR: packaging of '%s' failed\n", outputPath.string());
+                goto bail;
+            }
+        }
     }
 
     // If we've been asked to generate a dependency file, we need to finish up here.
diff --git a/tools/aapt/ConfigDescription.h b/tools/aapt/ConfigDescription.h
new file mode 100644
index 0000000..779c423
--- /dev/null
+++ b/tools/aapt/ConfigDescription.h
@@ -0,0 +1,57 @@
+/*
+ * 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.
+ */
+
+#ifndef __CONFIG_DESCRIPTION_H
+#define __CONFIG_DESCRIPTION_H
+
+#include <androidfw/ResourceTypes.h>
+
+/**
+ * Subclass of ResTable_config that adds convenient
+ * initialization and comparison methods.
+ */
+struct ConfigDescription : public android::ResTable_config {
+    ConfigDescription() {
+        memset(this, 0, sizeof(*this));
+        size = sizeof(android::ResTable_config);
+    }
+    ConfigDescription(const android::ResTable_config&o) {
+        *static_cast<android::ResTable_config*>(this) = o;
+        size = sizeof(android::ResTable_config);
+    }
+    ConfigDescription(const ConfigDescription&o) {
+        *static_cast<android::ResTable_config*>(this) = o;
+    }
+
+    ConfigDescription& operator=(const android::ResTable_config& o) {
+        *static_cast<android::ResTable_config*>(this) = o;
+        size = sizeof(android::ResTable_config);
+        return *this;
+    }
+    ConfigDescription& operator=(const ConfigDescription& o) {
+        *static_cast<android::ResTable_config*>(this) = o;
+        return *this;
+    }
+
+    inline bool operator<(const ConfigDescription& o) const { return compare(o) < 0; }
+    inline bool operator<=(const ConfigDescription& o) const { return compare(o) <= 0; }
+    inline bool operator==(const ConfigDescription& o) const { return compare(o) == 0; }
+    inline bool operator!=(const ConfigDescription& o) const { return compare(o) != 0; }
+    inline bool operator>=(const ConfigDescription& o) const { return compare(o) >= 0; }
+    inline bool operator>(const ConfigDescription& o) const { return compare(o) > 0; }
+};
+
+#endif // __CONFIG_DESCRIPTION_H
diff --git a/tools/aapt/Main.cpp b/tools/aapt/Main.cpp
index 1cf4783..5a60014 100644
--- a/tools/aapt/Main.cpp
+++ b/tools/aapt/Main.cpp
@@ -70,6 +70,7 @@
         "        [-F apk-file] [-J R-file-dir] \\\n"
         "        [--product product1,product2,...] \\\n"
         "        [-c CONFIGS] [--preferred-configurations CONFIGS] \\\n"
+        "        [--split CONFIGS [--split CONFIGS]] \\\n"
         "        [raw-files-dir [raw-files-dir] ...] \\\n"
         "        [--output-text-symbols DIR]\n"
         "\n"
@@ -166,10 +167,12 @@
         "       generate dependency files in the same directories for R.java and resource package\n"
         "   --auto-add-overlay\n"
         "       Automatically add resources that are only in overlays.\n"
-        "   --preferred-configurations\n"
-        "       Like the -c option for filtering out unneeded configurations, but\n"
-        "       only expresses a preference.  If there is no resource available with\n"
-        "       the preferred configuration then it will not be stripped.\n"
+        "   --preferred-density\n"
+        "       Specifies a preference for a particular density. Resources that do not\n"
+        "       match this density and have variants that are a closer match are removed.\n"
+        "   --split\n"
+        "       Builds a separate split APK for the configurations listed. This can\n"
+        "       be loaded alongside the base APK at runtime.\n"
         "   --rename-manifest-package\n"
         "       Rewrite the manifest so that its package name is the package name\n"
         "       given here.  Relative class names (for example .Foo) will be\n"
@@ -568,15 +571,24 @@
                     bundle.setGenDependencies(true);
                 } else if (strcmp(cp, "-utf16") == 0) {
                     bundle.setWantUTF16(true);
-                } else if (strcmp(cp, "-preferred-configurations") == 0) {
+                } else if (strcmp(cp, "-preferred-density") == 0) {
                     argc--;
                     argv++;
                     if (!argc) {
-                        fprintf(stderr, "ERROR: No argument supplied for '--preferred-configurations' option\n");
+                        fprintf(stderr, "ERROR: No argument supplied for '--preferred-density' option\n");
                         wantUsage = true;
                         goto bail;
                     }
-                    bundle.addPreferredConfigurations(argv[0]);
+                    bundle.setPreferredDensity(argv[0]);
+                } else if (strcmp(cp, "-split") == 0) {
+                    argc--;
+                    argv++;
+                    if (!argc) {
+                        fprintf(stderr, "ERROR: No argument supplied for '--split' option\n");
+                        wantUsage = true;
+                        goto bail;
+                    }
+                    bundle.addSplitConfigurations(argv[0]);
                 } else if (strcmp(cp, "-rename-manifest-package") == 0) {
                     argc--;
                     argv++;
diff --git a/tools/aapt/Main.h b/tools/aapt/Main.h
index a6b39ac..34c4496 100644
--- a/tools/aapt/Main.h
+++ b/tools/aapt/Main.h
@@ -10,8 +10,12 @@
 #include <utils/threads.h>
 #include <utils/List.h>
 #include <utils/Errors.h>
-#include "Bundle.h"
+#include <utils/StrongPointer.h>
+
 #include "AaptAssets.h"
+#include "ApkBuilder.h"
+#include "Bundle.h"
+#include "ResourceFilter.h"
 #include "ZipFile.h"
 
 
@@ -22,6 +26,8 @@
     #include <time.h>
 #endif /* BENCHMARK */
 
+class OutputSet;
+
 extern int doVersion(Bundle* bundle);
 extern int doList(Bundle* bundle);
 extern int doDump(Bundle* bundle);
@@ -34,13 +40,13 @@
 extern int calcPercent(long uncompressedLen, long compressedLen);
 
 extern android::status_t writeAPK(Bundle* bundle,
-    const sp<AaptAssets>& assets,
-    const android::String8& outputFile);
+    const android::String8& outputFile,
+    const android::sp<OutputSet>& outputSet);
 
 extern android::status_t updatePreProcessedCache(Bundle* bundle);
 
 extern android::status_t buildResources(Bundle* bundle,
-    const sp<AaptAssets>& assets);
+    const sp<AaptAssets>& assets, sp<ApkBuilder>& builder);
 
 extern android::status_t writeResourceSymbols(Bundle* bundle,
     const sp<AaptAssets>& assets, const String8& pkgName, bool includePrivate);
@@ -49,8 +55,6 @@
 
 extern bool isValidResourceType(const String8& type);
 
-ssize_t processAssets(Bundle* bundle, ZipFile* zip, const sp<AaptAssets>& assets);
-
 extern status_t filterResources(Bundle* bundle, const sp<AaptAssets>& assets);
 
 int dumpResources(Bundle* bundle);
diff --git a/tools/aapt/OutputSet.h b/tools/aapt/OutputSet.h
new file mode 100644
index 0000000..ea9ef70
--- /dev/null
+++ b/tools/aapt/OutputSet.h
@@ -0,0 +1,56 @@
+/*
+ * 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.
+ */
+
+#ifndef __OUTPUT_SET_H
+#define __OUTPUT_SET_H
+
+#include <set>
+#include <utils/Errors.h>
+#include <utils/String8.h>
+#include <utils/StrongPointer.h>
+
+class AaptFile;
+
+class OutputEntry {
+public:
+    OutputEntry() {}
+    OutputEntry(const android::String8& path, const android::sp<const AaptFile>& file)
+        : mPath(path), mFile(file) {}
+
+    inline const android::sp<const AaptFile>& getFile() const {
+        return mFile;
+    }
+
+    inline const android::String8& getPath() const {
+        return mPath;
+    }
+
+    bool operator<(const OutputEntry& o) const { return getPath() < o.mPath; }
+    bool operator==(const OutputEntry& o) const { return getPath() == o.mPath; }
+
+private:
+    android::String8 mPath;
+    android::sp<const AaptFile> mFile;
+};
+
+class OutputSet : public virtual android::RefBase {
+public:
+    virtual const std::set<OutputEntry>& getEntries() const = 0;
+
+    virtual ~OutputSet() {}
+};
+
+#endif // __OUTPUT_SET_H
diff --git a/tools/aapt/Package.cpp b/tools/aapt/Package.cpp
index 872d95c..dc16e35 100644
--- a/tools/aapt/Package.cpp
+++ b/tools/aapt/Package.cpp
@@ -5,6 +5,7 @@
 //
 #include "Main.h"
 #include "AaptAssets.h"
+#include "OutputSet.h"
 #include "ResourceTable.h"
 #include "ResourceFilter.h"
 
@@ -36,11 +37,8 @@
 };
 
 /* fwd decls, so I can write this downward */
-ssize_t processAssets(Bundle* bundle, ZipFile* zip, const sp<AaptAssets>& assets);
-ssize_t processAssets(Bundle* bundle, ZipFile* zip, const sp<AaptDir>& dir,
-                        const AaptGroupEntry& ge, const ResourceFilter* filter);
-bool processFile(Bundle* bundle, ZipFile* zip,
-                        const sp<AaptGroup>& group, const sp<AaptFile>& file);
+ssize_t processAssets(Bundle* bundle, ZipFile* zip, const sp<const OutputSet>& outputSet);
+bool processFile(Bundle* bundle, ZipFile* zip, String8 storageName, const sp<const AaptFile>& file);
 bool okayToCompress(Bundle* bundle, const String8& pathName);
 ssize_t processJarFiles(Bundle* bundle, ZipFile* zip);
 
@@ -51,8 +49,7 @@
  * On success, "bundle->numPackages" will be the number of Zip packages
  * we created.
  */
-status_t writeAPK(Bundle* bundle, const sp<AaptAssets>& assets,
-                       const String8& outputFile)
+status_t writeAPK(Bundle* bundle, const String8& outputFile, const sp<OutputSet>& outputSet)
 {
     #if BENCHMARK
     fprintf(stdout, "BENCHMARK: Starting APK Bundling \n");
@@ -112,7 +109,7 @@
         printf("Writing all files...\n");
     }
 
-    count = processAssets(bundle, zip, assets);
+    count = processAssets(bundle, zip, outputSet);
     if (count < 0) {
         fprintf(stderr, "ERROR: unable to process assets while packaging '%s'\n",
                 outputFile.string());
@@ -218,72 +215,24 @@
     return result;
 }
 
-ssize_t processAssets(Bundle* bundle, ZipFile* zip,
-                      const sp<AaptAssets>& assets)
-{
-    ResourceFilter filter;
-    status_t status = filter.parse(bundle->getConfigurations());
-    if (status != NO_ERROR) {
-        return -1;
-    }
-
-    ssize_t count = 0;
-
-    const size_t N = assets->getGroupEntries().size();
-    for (size_t i=0; i<N; i++) {
-        const AaptGroupEntry& ge = assets->getGroupEntries()[i];
-
-        ssize_t res = processAssets(bundle, zip, assets, ge, &filter);
-        if (res < 0) {
-            return res;
-        }
-
-        count += res;
-    }
-
-    return count;
-}
-
-ssize_t processAssets(Bundle* bundle, ZipFile* zip, const sp<AaptDir>& dir,
-        const AaptGroupEntry& ge, const ResourceFilter* filter)
+ssize_t processAssets(Bundle* bundle, ZipFile* zip, const sp<const OutputSet>& outputSet)
 {
     ssize_t count = 0;
-
-    const size_t ND = dir->getDirs().size();
-    size_t i;
-    for (i=0; i<ND; i++) {
-        const sp<AaptDir>& subDir = dir->getDirs().valueAt(i);
-
-        const bool filterable = filter != NULL && subDir->getLeaf().find("mipmap-") != 0;
-
-        if (filterable && subDir->getLeaf() != subDir->getPath() && !filter->match(ge.toParams())) {
-            continue;
-        }
-
-        ssize_t res = processAssets(bundle, zip, subDir, ge, filterable ? filter : NULL);
-        if (res < 0) {
-            return res;
-        }
-        count += res;
-    }
-
-    if (filter != NULL && !filter->match(ge.toParams())) {
-        return count;
-    }
-
-    const size_t NF = dir->getFiles().size();
-    for (i=0; i<NF; i++) {
-        sp<AaptGroup> gp = dir->getFiles().valueAt(i);
-        ssize_t fi = gp->getFiles().indexOfKey(ge);
-        if (fi >= 0) {
-            sp<AaptFile> fl = gp->getFiles().valueAt(fi);
-            if (!processFile(bundle, zip, gp, fl)) {
+    const std::set<OutputEntry>& entries = outputSet->getEntries();
+    std::set<OutputEntry>::const_iterator iter = entries.begin();
+    for (; iter != entries.end(); iter++) {
+        const OutputEntry& entry = *iter;
+        if (entry.getFile() == NULL) {
+            fprintf(stderr, "warning: null file being processed.\n");
+        } else {
+            String8 storagePath(entry.getPath());
+            storagePath.convertToResPath();
+            if (!processFile(bundle, zip, storagePath, entry.getFile())) {
                 return UNKNOWN_ERROR;
             }
             count++;
         }
     }
-
     return count;
 }
 
@@ -294,12 +243,10 @@
  * delete the existing entry before adding the new one.
  */
 bool processFile(Bundle* bundle, ZipFile* zip,
-                 const sp<AaptGroup>& group, const sp<AaptFile>& file)
+                 String8 storageName, const sp<const AaptFile>& file)
 {
     const bool hasData = file->hasData();
 
-    String8 storageName(group->getPath());
-    storageName.convertToResPath();
     ZipEntry* entry;
     bool fromGzip = false;
     status_t result;
@@ -403,8 +350,8 @@
             fprintf(stderr, "      Unable to add '%s': file already in archive (try '-u'?)\n",
                     file->getPrintableSource().string());
         } else {
-            fprintf(stderr, "      Unable to add '%s': Zip add failed\n", 
-                    file->getPrintableSource().string());
+            fprintf(stderr, "      Unable to add '%s': Zip add failed (%d)\n",
+                    file->getPrintableSource().string(), result);
         }
         return false;
     }
diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp
index 1348be3..e599643 100644
--- a/tools/aapt/Resource.cpp
+++ b/tools/aapt/Resource.cpp
@@ -179,24 +179,6 @@
         || type == "color" || type == "menu" || type == "mipmap";
 }
 
-static sp<AaptFile> getResourceFile(const sp<AaptAssets>& assets, bool makeIfNecessary=true)
-{
-    sp<AaptGroup> group = assets->getFiles().valueFor(String8("resources.arsc"));
-    sp<AaptFile> file;
-    if (group != NULL) {
-        file = group->getFiles().valueFor(AaptGroupEntry());
-        if (file != NULL) {
-            return file;
-        }
-    }
-
-    if (!makeIfNecessary) {
-        return NULL;
-    }
-    return assets->addFile(String8("resources.arsc"), AaptGroupEntry(), String8(),
-                            NULL, String8());
-}
-
 static status_t parsePackage(Bundle* bundle, const sp<AaptAssets>& assets,
     const sp<AaptGroup>& grp)
 {
@@ -359,23 +341,6 @@
     return (hasErrors || (res < NO_ERROR)) ? UNKNOWN_ERROR : NO_ERROR;
 }
 
-status_t postProcessImages(const sp<AaptAssets>& assets,
-                           ResourceTable* table,
-                           const sp<ResourceTypeSet>& set)
-{
-    ResourceDirIterator it(set, String8("drawable"));
-    bool hasErrors = false;
-    ssize_t res;
-    while ((res=it.next()) == NO_ERROR) {
-        res = postProcessImage(assets, table, it.getFile());
-        if (res < NO_ERROR) {
-            hasErrors = true;
-        }
-    }
-
-    return (hasErrors || (res < NO_ERROR)) ? UNKNOWN_ERROR : NO_ERROR;
-}
-
 static void collect_files(const sp<AaptDir>& dir,
         KeyedVector<String8, sp<ResourceTypeSet> >* resources)
 {
@@ -906,7 +871,38 @@
     return 0;
 }
 
-status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets)
+status_t generateAndroidManifestForSplit(const String16& package, const sp<ApkSplit>& split,
+        sp<AaptFile>& outFile) {
+    const String8 filename("AndroidManifest.xml");
+    const String16 androidPrefix("android");
+    const String16 androidNSUri("http://schemas.android.com/apk/res/android");
+    sp<XMLNode> root = XMLNode::newNamespace(filename, androidPrefix, androidNSUri);
+
+    // Build the <manifest> tag
+    sp<XMLNode> manifest = XMLNode::newElement(filename, String16(), String16("manifest"));
+
+    // Add the 'package' attribute which is set to the original package name.
+    manifest->addAttribute(String16(), String16("package"), package);
+
+    // Add the 'split' attribute which describes the configurations included.
+    String8 splitName("config_");
+    splitName.append(split->getDirectorySafeName());
+    manifest->addAttribute(String16(), String16("split"), String16(splitName));
+
+    // Build an empty <application> tag (required).
+    sp<XMLNode> app = XMLNode::newElement(filename, String16(), String16("application"));
+    manifest->addChild(app);
+    root->addChild(manifest);
+
+    status_t err = root->flatten(outFile, true, true);
+    if (err != NO_ERROR) {
+        return err;
+    }
+    outFile->setCompressionMethod(ZipEntry::kCompressDeflated);
+    return NO_ERROR;
+}
+
+status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets, sp<ApkBuilder>& builder)
 {
     // First, look for a package file to parse.  This is required to
     // be able to generate the resource information.
@@ -1122,12 +1118,6 @@
     // --------------------------------------------------------------------
 
     if (table.hasResources()) {
-        sp<AaptFile> resFile(getResourceFile(assets));
-        if (resFile == NULL) {
-            fprintf(stderr, "Error: unable to generate entry for resource data\n");
-            return UNKNOWN_ERROR;
-        }
-
         err = table.assignResourceIds();
         if (err < NO_ERROR) {
             return err;
@@ -1235,16 +1225,24 @@
     }
 
     if (drawables != NULL) {
-        err = postProcessImages(assets, &table, drawables);
-        if (err != NO_ERROR) {
+        ResourceDirIterator it(drawables, String8("drawable"));
+        while ((err=it.next()) == NO_ERROR) {
+            err = postProcessImage(assets, &table, it.getFile());
+            if (err != NO_ERROR) {
+                hasErrors = true;
+            }
+        }
+
+        if (err < NO_ERROR) {
             hasErrors = true;
         }
+        err = NO_ERROR;
     }
 
     if (colors != NULL) {
         ResourceDirIterator it(colors, String8("color"));
         while ((err=it.next()) == NO_ERROR) {
-          err = compileXmlFile(assets, it.getFile(), &table, xmlFlags);
+            err = compileXmlFile(assets, it.getFile(), &table, xmlFlags);
             if (err != NO_ERROR) {
                 hasErrors = true;
             }
@@ -1320,15 +1318,35 @@
             return err;
         }
 
-        resFile = getResourceFile(assets);
-        if (resFile == NULL) {
-            fprintf(stderr, "Error: unable to generate entry for resource data\n");
-            return UNKNOWN_ERROR;
-        }
+        Vector<sp<ApkSplit> >& splits = builder->getSplits();
+        const size_t numSplits = splits.size();
+        for (size_t i = 0; i < numSplits; i++) {
+            sp<ApkSplit>& split = splits.editItemAt(i);
+            sp<AaptFile> flattenedTable = new AaptFile(String8("resources.arsc"),
+                    AaptGroupEntry(), String8());
+            err = table.flatten(bundle, split->getResourceFilter(), flattenedTable);
+            if (err != NO_ERROR) {
+                fprintf(stderr, "Failed to generate resource table for split '%s'\n",
+                        split->getPrintableName().string());
+                return err;
+            }
+            split->addEntry(String8("resources.arsc"), flattenedTable);
 
-        err = table.flatten(bundle, resFile);
-        if (err < NO_ERROR) {
-            return err;
+            if (split->isBase()) {
+                resFile = flattenedTable;
+                finalResTable.add(flattenedTable->getData(), flattenedTable->getSize());
+            } else {
+                sp<AaptFile> generatedManifest = new AaptFile(String8("AndroidManifest.xml"),
+                        AaptGroupEntry(), String8());
+                err = generateAndroidManifestForSplit(String16(assets->getPackage()), split,
+                        generatedManifest);
+                if (err != NO_ERROR) {
+                    fprintf(stderr, "Failed to generate AndroidManifest.xml for split '%s'\n",
+                            split->getPrintableName().string());
+                    return err;
+                }
+                split->addEntry(String8("AndroidManifest.xml"), generatedManifest);
+            }
         }
 
         if (bundle->getPublicOutputFile()) {
@@ -1344,18 +1362,13 @@
             table.writePublicDefinitions(String16(assets->getPackage()), fp);
             fclose(fp);
         }
-        
-        // Read resources back in,
-        finalResTable.add(resFile->getData(), resFile->getSize());
-        
-#if 0
-        NOISY(
-              printf("Generated resources:\n");
-              finalResTable.print();
-        )
-#endif
+
+        if (finalResTable.getTableCount() == 0 || resFile == NULL) {
+            fprintf(stderr, "No resource table was generated.\n");
+            return UNKNOWN_ERROR;
+        }
     }
-    
+
     // Perform a basic validation of the manifest file.  This time we
     // parse it with the comments intact, so that we can use them to
     // generate java docs...  so we are not going to write this one
diff --git a/tools/aapt/ResourceFilter.cpp b/tools/aapt/ResourceFilter.cpp
index 8ca852e..de8b4fc 100644
--- a/tools/aapt/ResourceFilter.cpp
+++ b/tools/aapt/ResourceFilter.cpp
@@ -1,119 +1,92 @@
 //
-// Copyright 2011 The Android Open Source Project
+// Copyright 2014 The Android Open Source Project
 //
 // Build resource files from raw assets.
 //
 
 #include "ResourceFilter.h"
+#include "AaptUtil.h"
+#include "AaptConfig.h"
 
 status_t
-ResourceFilter::parse(const char* arg)
+WeakResourceFilter::parse(const String8& str)
 {
-    if (arg == NULL) {
-        return 0;
-    }
-
-    const char* p = arg;
-    const char* q;
-
-    while (true) {
-        q = strchr(p, ',');
-        if (q == NULL) {
-            q = p + strlen(p);
-        }
-
-        String8 part(p, q-p);
-
+    Vector<String8> configStrs = AaptUtil::split(str, ',');
+    const size_t N = configStrs.size();
+    mConfigs.clear();
+    mConfigMask = 0;
+    mConfigs.resize(N);
+    for (size_t i = 0; i < N; i++) {
+        const String8& part = configStrs[i];
         if (part == "en_XA") {
             mContainsPseudoAccented = true;
         } else if (part == "ar_XB") {
             mContainsPseudoBidi = true;
         }
-        int axis;
-        AxisValue value;
-        if (!AaptGroupEntry::parseFilterNamePart(part, &axis, &value)) {
-            fprintf(stderr, "Invalid configuration: %s\n", arg);
-            fprintf(stderr, "                       ");
-            for (int i=0; i<p-arg; i++) {
-                fprintf(stderr, " ");
-            }
-            for (int i=0; i<q-p; i++) {
-                fprintf(stderr, "^");
-            }
-            fprintf(stderr, "\n");
-            return 1;
+
+        std::pair<ConfigDescription, uint32_t>& entry = mConfigs.editItemAt(i);
+
+        AaptLocaleValue val;
+        if (val.initFromFilterString(part)) {
+            // For backwards compatibility, we accept configurations that
+            // only specify locale in the standard 'en_US' format.
+            val.writeTo(&entry.first);
+        } else if (!AaptConfig::parse(part, &entry.first)) {
+            fprintf(stderr, "Invalid configuration: %s\n", part.string());
+            return UNKNOWN_ERROR;
         }
 
-        ssize_t index = mData.indexOfKey(axis);
-        if (index < 0) {
-            mData.add(axis, SortedVector<AxisValue>());
-        }
-        SortedVector<AxisValue>& sv = mData.editValueFor(axis);
-        sv.add(value);
+        entry.second = mDefault.diff(entry.first);
 
-        // If it's a locale with a region, script or variant, we should also match an
-        // unmodified locale of the same language
-        if (axis == AXIS_LOCALE) {
-            if (value.localeValue.region[0] || value.localeValue.script[0] ||
-                value.localeValue.variant[0]) {
-                AxisValue copy;
-                memcpy(copy.localeValue.language, value.localeValue.language,
-                       sizeof(value.localeValue.language));
-                sv.add(copy);
-            }
-        }
-        p = q;
-        if (!*p) break;
-        p++;
+        // Ignore the version
+        entry.second &= ~ResTable_config::CONFIG_VERSION;
+
+        mConfigMask |= entry.second;
     }
 
     return NO_ERROR;
 }
 
 bool
-ResourceFilter::isEmpty() const
+WeakResourceFilter::match(const ResTable_config& config) const
 {
-    return mData.size() == 0;
-}
-
-bool
-ResourceFilter::match(int axis, const AxisValue& value) const
-{
-    if (value.intValue == 0 && (value.localeValue.language[0] == 0)) {
-        // they didn't specify anything so take everything
+    uint32_t mask = mDefault.diff(config);
+    if ((mConfigMask & mask) == 0) {
+        // The two configurations don't have any common axis.
         return true;
     }
-    ssize_t index = mData.indexOfKey(axis);
-    if (index < 0) {
-        // we didn't request anything on this axis so take everything
-        return true;
-    }
-    const SortedVector<AxisValue>& sv = mData.valueAt(index);
-    return sv.indexOf(value) >= 0;
-}
 
-bool
-ResourceFilter::match(int axis, const ResTable_config& config) const
-{
-    return match(axis, AaptGroupEntry::getConfigValueForAxis(config, axis));
-}
-
-bool
-ResourceFilter::match(const ResTable_config& config) const
-{
-    for (int i=AXIS_START; i<=AXIS_END; i++) {
-        if (!match(i, AaptGroupEntry::getConfigValueForAxis(config, i))) {
-            return false;
+    const size_t N = mConfigs.size();
+    for (size_t i = 0; i < N; i++) {
+        const std::pair<ConfigDescription, uint32_t>& entry = mConfigs[i];
+        uint32_t diff = entry.first.diff(config);
+        if ((diff & entry.second) == 0) {
+            return true;
+        } else if ((diff & entry.second) == ResTable_config::CONFIG_LOCALE) {
+            // If the locales differ, but the languages are the same and
+            // the locale we are matching only has a language specified,
+            // we match.
+            if (config.language[0] && memcmp(config.language, entry.first.language, sizeof(config.language)) == 0) {
+                if (config.country[0] == 0) {
+                    return true;
+                }
+            }
         }
     }
-    return true;
+    return false;
 }
 
-const SortedVector<AxisValue>* ResourceFilter::configsForAxis(int axis) const
-{
-    ssize_t index = mData.indexOfKey(axis);
-    if (index < 0) {
-        return NULL;
+status_t
+StrongResourceFilter::parse(const String8& str) {
+    Vector<String8> configStrs = AaptUtil::split(str, ',');
+    ConfigDescription config;
+    mConfigs.clear();
+    for (size_t i = 0; i < configStrs.size(); i++) {
+        if (!AaptConfig::parse(configStrs[i], &config)) {
+            fprintf(stderr, "Invalid configuration: %s\n", configStrs[i].string());
+            return UNKNOWN_ERROR;
+        }
+        mConfigs.insert(config);
     }
-    return &mData.valueAt(index);
+    return NO_ERROR;
 }
diff --git a/tools/aapt/ResourceFilter.h b/tools/aapt/ResourceFilter.h
index c57770e..f459584 100644
--- a/tools/aapt/ResourceFilter.h
+++ b/tools/aapt/ResourceFilter.h
@@ -7,31 +7,137 @@
 #ifndef RESOURCE_FILTER_H
 #define RESOURCE_FILTER_H
 
+#include <androidfw/ResourceTypes.h>
+#include <set>
+#include <utility>
+#include <utils/Errors.h>
+#include <utils/String8.h>
+#include <utils/StrongPointer.h>
+#include <utils/Vector.h>
+
 #include "AaptAssets.h"
+#include "ConfigDescription.h"
+
+class ResourceFilter : public virtual android::RefBase {
+public:
+    virtual bool match(const android::ResTable_config& config) const = 0;
+};
 
 /**
  * Implements logic for parsing and handling "-c" and "--preferred-configurations"
  * options.
  */
-class ResourceFilter
-{
+class WeakResourceFilter : public ResourceFilter {
 public:
-    ResourceFilter() : mData(), mContainsPseudoAccented(false),
-        mContainsPseudoBidi(false) {}
-    status_t parse(const char* arg);
-    bool isEmpty() const;
-    bool match(int axis, const ResTable_config& config) const;
-    bool match(const ResTable_config& config) const;
-    const SortedVector<AxisValue>* configsForAxis(int axis) const;
-    inline bool containsPseudo() const { return mContainsPseudoAccented; }
-    inline bool containsPseudoBidi() const { return mContainsPseudoBidi; }
+    WeakResourceFilter()
+        : mContainsPseudoAccented(false)
+        , mContainsPseudoBidi(false) {}
+
+    android::status_t parse(const android::String8& str);
+
+    bool match(const android::ResTable_config& config) const;
+
+    inline bool isEmpty() const {
+        return mConfigMask == 0;
+    }
+
+    inline bool containsPseudo() const {
+        return mContainsPseudoAccented;
+    }
+
+    inline bool containsPseudoBidi() const {
+        return mContainsPseudoBidi;
+    }
 
 private:
-    bool match(int axis, const AxisValue& value) const;
+    ConfigDescription mDefault;
+    uint32_t mConfigMask;
+    android::Vector<std::pair<ConfigDescription, uint32_t> > mConfigs;
 
-    KeyedVector<int,SortedVector<AxisValue> > mData;
     bool mContainsPseudoAccented;
     bool mContainsPseudoBidi;
 };
 
+/**
+ * Matches resources that have at least one of the configurations
+ * that this filter is looking for. In order to match a configuration,
+ * the resource must have the exact same configuration.
+ *
+ * This filter acts as a logical OR when matching resources.
+ *
+ * For example, if the filter is looking for resources with
+ * fr-land, de-land, or sw600dp:
+ *
+ * (PASS) fr-land
+ * (FAIL) fr
+ * (PASS) de-land
+ * (FAIL) de
+ * (FAIL) de-sw600dp
+ * (PASS) sw600dp
+ * (FAIL) sw600dp-land
+ */
+class StrongResourceFilter : public ResourceFilter {
+public:
+    StrongResourceFilter() {}
+    StrongResourceFilter(const std::set<ConfigDescription>& configs)
+        : mConfigs(configs) {}
+
+    android::status_t parse(const android::String8& str);
+
+    bool match(const android::ResTable_config& config) const {
+        std::set<ConfigDescription>::const_iterator iter = mConfigs.begin();
+        for (; iter != mConfigs.end(); iter++) {
+            if (iter->compare(config) == 0) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    inline const std::set<ConfigDescription>& getConfigs() const {
+        return mConfigs;
+    }
+
+private:
+    std::set<ConfigDescription> mConfigs;
+};
+
+/**
+ * Negates the response of the target filter.
+ */
+class InverseResourceFilter : public ResourceFilter {
+public:
+    InverseResourceFilter(const android::sp<ResourceFilter>& filter)
+        : mFilter(filter) {}
+
+    bool match(const android::ResTable_config& config) const {
+        return !mFilter->match(config);
+    }
+
+private:
+    const android::sp<ResourceFilter> mFilter;
+};
+
+/**
+ * A logical AND of all the added filters.
+ */
+class AndResourceFilter : public ResourceFilter {
+public:
+    void addFilter(const android::sp<ResourceFilter>& filter) {
+        mFilters.add(filter);
+    }
+
+    bool match(const android::ResTable_config& config) const {
+        const size_t N = mFilters.size();
+        for (size_t i = 0; i < N; i++) {
+            if (!mFilters[i]->match(config)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+private:
+    android::Vector<android::sp<ResourceFilter> > mFilters;
+};
 #endif
diff --git a/tools/aapt/ResourceTable.cpp b/tools/aapt/ResourceTable.cpp
index 6eab65b..efbba40 100644
--- a/tools/aapt/ResourceTable.cpp
+++ b/tools/aapt/ResourceTable.cpp
@@ -2095,10 +2095,10 @@
     return mNumLocal > 0;
 }
 
-sp<AaptFile> ResourceTable::flatten(Bundle* bundle)
+sp<AaptFile> ResourceTable::flatten(Bundle* bundle, const sp<const ResourceFilter>& filter)
 {
     sp<AaptFile> data = new AaptFile(String8(), AaptGroupEntry(), String8());
-    status_t err = flatten(bundle, data);
+    status_t err = flatten(bundle, filter, data);
     return err == NO_ERROR ? data : NULL;
 }
 
@@ -2658,8 +2658,8 @@
         }
 
         // Check that all requested localizations are present for this string
-        if (mBundle->getConfigurations() != NULL && mBundle->getRequireLocalization()) {
-            const char* allConfigs = mBundle->getConfigurations();
+        if (mBundle->getConfigurations().size() > 0 && mBundle->getRequireLocalization()) {
+            const char* allConfigs = mBundle->getConfigurations().string();
             const char* start = allConfigs;
             const char* comma;
             
@@ -2713,14 +2713,8 @@
     return err;
 }
 
-status_t ResourceTable::flatten(Bundle* bundle, const sp<AaptFile>& dest)
+status_t ResourceTable::flatten(Bundle* bundle, const sp<const ResourceFilter>& filter, const sp<AaptFile>& dest)
 {
-    ResourceFilter filter;
-    status_t err = filter.parse(bundle->getConfigurations());
-    if (err != NO_ERROR) {
-        return err;
-    }
-
     const ConfigDescription nullConfig;
 
     const size_t N = mOrderedPackages.size();
@@ -2795,7 +2789,7 @@
                 const size_t N = c->getEntries().size();
                 for (size_t ei=0; ei<N; ei++) {
                     ConfigDescription config = c->getEntries().keyAt(ei);
-                    if (filterable && !filter.match(config)) {
+                    if (filterable && !filter->match(config)) {
                         continue;
                     }
                     sp<Entry> e = c->getEntries().valueAt(ei);
@@ -2887,7 +2881,7 @@
             return amt;
         }
 
-        err = flattenLibraryTable(data, libraryPackages);
+        status_t err = flattenLibraryTable(data, libraryPackages);
         if (err != NO_ERROR) {
             fprintf(stderr, "ERROR: failed to write library table\n");
             return err;
@@ -2943,11 +2937,11 @@
                     }
                     const size_t CN = cl->getEntries().size();
                     for (size_t ci=0; ci<CN; ci++) {
-                        if (filterable && !filter.match(cl->getEntries().keyAt(ci))) {
+                        if (filterable && !filter->match(cl->getEntries().keyAt(ci))) {
                             continue;
                         }
                         for (size_t cj=ci+1; cj<CN; cj++) {
-                            if (filterable && !filter.match(cl->getEntries().keyAt(cj))) {
+                            if (filterable && !filter->match(cl->getEntries().keyAt(cj))) {
                                 continue;
                             }
                             typeSpecFlags[ei] |= htodl(
@@ -2989,7 +2983,7 @@
                       config.screenHeightDp,
                       config.layoutDirection));
                       
-                if (filterable && !filter.match(config)) {
+                if (filterable && !filter->match(config)) {
                     continue;
                 }
                 
@@ -3108,7 +3102,7 @@
     }
     
     ssize_t strStart = dest->getSize();
-    err = valueStrings.writeStringBlock(dest);
+    status_t err = valueStrings.writeStringBlock(dest);
     if (err != NO_ERROR) {
         return err;
     }
diff --git a/tools/aapt/ResourceTable.h b/tools/aapt/ResourceTable.h
index ec8fd175e..a73993c 100644
--- a/tools/aapt/ResourceTable.h
+++ b/tools/aapt/ResourceTable.h
@@ -7,8 +7,10 @@
 #ifndef RESOURCE_TABLE_H
 #define RESOURCE_TABLE_H
 
+#include "ConfigDescription.h"
 #include "StringPool.h"
 #include "SourcePos.h"
+#include "ResourceFilter.h"
 
 #include <set>
 #include <map>
@@ -76,37 +78,6 @@
     class Type;
     class Entry;
 
-    struct ConfigDescription : public ResTable_config {
-        ConfigDescription() {
-            memset(this, 0, sizeof(*this));
-            size = sizeof(ResTable_config);
-        }
-        ConfigDescription(const ResTable_config&o) {
-            *static_cast<ResTable_config*>(this) = o;
-            size = sizeof(ResTable_config);
-        }
-        ConfigDescription(const ConfigDescription&o) {
-            *static_cast<ResTable_config*>(this) = o;
-        }
-
-        ConfigDescription& operator=(const ResTable_config& o) {
-            *static_cast<ResTable_config*>(this) = o;
-            size = sizeof(ResTable_config);
-            return *this;
-        }
-        ConfigDescription& operator=(const ConfigDescription& o) {
-            *static_cast<ResTable_config*>(this) = o;
-            return *this;
-        }
-
-        inline bool operator<(const ConfigDescription& o) const { return compare(o) < 0; }
-        inline bool operator<=(const ConfigDescription& o) const { return compare(o) <= 0; }
-        inline bool operator==(const ConfigDescription& o) const { return compare(o) == 0; }
-        inline bool operator!=(const ConfigDescription& o) const { return compare(o) != 0; }
-        inline bool operator>=(const ConfigDescription& o) const { return compare(o) >= 0; }
-        inline bool operator>(const ConfigDescription& o) const { return compare(o) > 0; }
-    };
-
     ResourceTable(Bundle* bundle, const String16& assetsPackage);
 
     status_t addIncludedResources(Bundle* bundle, const sp<AaptAssets>& assets);
@@ -182,7 +153,7 @@
     size_t numLocalResources() const;
     bool hasResources() const;
 
-    sp<AaptFile> flatten(Bundle*);
+    sp<AaptFile> flatten(Bundle* bundle, const sp<const ResourceFilter>& filter);
 
     static inline uint32_t makeResId(uint32_t packageId,
                                      uint32_t typeId,
@@ -223,7 +194,7 @@
     void addLocalization(const String16& name, const String8& locale, const SourcePos& src);
     status_t validateLocalizations(void);
 
-    status_t flatten(Bundle*, const sp<AaptFile>& dest);
+    status_t flatten(Bundle* bundle, const sp<const ResourceFilter>& filter, const sp<AaptFile>& dest);
     status_t flattenLibraryTable(const sp<AaptFile>& dest, const Vector<sp<Package> >& libs);
 
     void writePublicDefinitions(const String16& package, FILE* fp);
diff --git a/tools/aapt/tests/AaptConfig_test.cpp b/tools/aapt/tests/AaptConfig_test.cpp
new file mode 100644
index 0000000..e795d818
--- /dev/null
+++ b/tools/aapt/tests/AaptConfig_test.cpp
@@ -0,0 +1,78 @@
+/*
+ * 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.
+ */
+
+#include <utils/String8.h>
+#include <gtest/gtest.h>
+
+#include "AaptConfig.h"
+#include "ConfigDescription.h"
+#include "TestHelper.h"
+
+using android::String8;
+
+static ::testing::AssertionResult TestParse(const String8& input, ConfigDescription* config=NULL) {
+    if (AaptConfig::parse(String8(input), config)) {
+        return ::testing::AssertionSuccess() << input << " was successfully parsed";
+    }
+    return ::testing::AssertionFailure() << input << " could not be parsed";
+}
+
+static ::testing::AssertionResult TestParse(const char* input, ConfigDescription* config=NULL) {
+    return TestParse(String8(input), config);
+}
+
+TEST(AaptConfigTest, ParseFailWhenQualifiersAreOutOfOrder) {
+    EXPECT_FALSE(TestParse("en-sw600dp-ldrtl"));
+    EXPECT_FALSE(TestParse("land-en"));
+    EXPECT_FALSE(TestParse("hdpi-320dpi"));
+}
+
+TEST(AaptConfigTest, ParseFailWhenQualifiersAreNotMatched) {
+    EXPECT_FALSE(TestParse("en-sw600dp-ILLEGAL"));
+}
+
+TEST(AaptConfigTest, ParseFailWhenQualifiersHaveTrailingDash) {
+    EXPECT_FALSE(TestParse("en-sw600dp-land-"));
+}
+
+TEST(AaptConfigTest, ParseBasicQualifiers) {
+    ConfigDescription config;
+    EXPECT_TRUE(TestParse("", &config));
+    EXPECT_EQ(String8(""), config.toString());
+
+    EXPECT_TRUE(TestParse("fr-land", &config));
+    EXPECT_EQ(String8("fr-land"), config.toString());
+
+    EXPECT_TRUE(TestParse("mcc310-pl-sw720dp-normal-long-port-night-"
+                "xhdpi-keyssoft-qwerty-navexposed-nonav", &config));
+    EXPECT_EQ(String8("mcc310-pl-sw720dp-normal-long-port-night-"
+                "xhdpi-keyssoft-qwerty-navexposed-nonav-v13"), config.toString());
+}
+
+TEST(AaptConfigTest, ParseLocales) {
+    ConfigDescription config;
+    EXPECT_TRUE(TestParse("en-rUS", &config));
+    EXPECT_EQ(String8("en-US"), config.toString());
+}
+
+TEST(AaptConfigTest, ParseQualifierAddedInApi13) {
+    ConfigDescription config;
+    EXPECT_TRUE(TestParse("sw600dp", &config));
+    EXPECT_EQ(String8("sw600dp-v13"), config.toString());
+
+    EXPECT_TRUE(TestParse("sw600dp-v8", &config));
+    EXPECT_EQ(String8("sw600dp-v13"), config.toString());
+}
diff --git a/tools/aapt/tests/AaptGroupEntry_test.cpp b/tools/aapt/tests/AaptGroupEntry_test.cpp
new file mode 100644
index 0000000..7348a08
--- /dev/null
+++ b/tools/aapt/tests/AaptGroupEntry_test.cpp
@@ -0,0 +1,54 @@
+/*
+ * 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.
+ */
+
+#include <utils/String8.h>
+#include <gtest/gtest.h>
+
+#include "AaptAssets.h"
+#include "ResourceFilter.h"
+#include "TestHelper.h"
+
+using android::String8;
+
+static ::testing::AssertionResult TestParse(AaptGroupEntry& entry, const String8& dirName,
+        String8* outType) {
+    if (entry.initFromDirName(dirName, outType)) {
+        return ::testing::AssertionSuccess() << dirName << " was successfully parsed";
+    }
+    return ::testing::AssertionFailure() << dirName << " could not be parsed";
+}
+
+static ::testing::AssertionResult TestParse(AaptGroupEntry& entry, const char* input,
+        String8* outType) {
+    return TestParse(entry, String8(input), outType);
+}
+
+TEST(AaptGroupEntryTest, ParseNoQualifier) {
+    AaptGroupEntry entry;
+    String8 type;
+    EXPECT_TRUE(TestParse(entry, "menu", &type));
+    EXPECT_EQ(String8("menu"), type);
+}
+
+TEST(AaptGroupEntryTest, ParseCorrectType) {
+    AaptGroupEntry entry;
+    String8 type;
+    EXPECT_TRUE(TestParse(entry, "anim", &type));
+    EXPECT_EQ(String8("anim"), type);
+
+    EXPECT_TRUE(TestParse(entry, "animator", &type));
+    EXPECT_EQ(String8("animator"), type);
+}
diff --git a/tools/aapt/tests/ResourceFilter_test.cpp b/tools/aapt/tests/ResourceFilter_test.cpp
new file mode 100644
index 0000000..30697bb
--- /dev/null
+++ b/tools/aapt/tests/ResourceFilter_test.cpp
@@ -0,0 +1,128 @@
+/*
+ * 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.
+ */
+
+#include <androidfw/ResourceTypes.h>
+#include <utils/String8.h>
+#include <gtest/gtest.h>
+
+#include "AaptConfig.h"
+#include "ResourceFilter.h"
+#include "ConfigDescription.h"
+
+using android::String8;
+
+// In this context, 'Axis' represents a particular field in the configuration,
+// such as language or density.
+
+TEST(WeakResourceFilterTest, EmptyFilterMatchesAnything) {
+    WeakResourceFilter filter;
+    ASSERT_EQ(NO_ERROR, filter.parse(String8("")));
+
+    ConfigDescription config;
+    config.density = 320;
+
+    EXPECT_TRUE(filter.match(config));
+
+    config.language[0] = 'f';
+    config.language[1] = 'r';
+
+    EXPECT_TRUE(filter.match(config));
+}
+
+TEST(WeakResourceFilterTest, MatchesConfigWithUnrelatedAxis) {
+    WeakResourceFilter filter;
+    ASSERT_EQ(NO_ERROR, filter.parse(String8("fr")));
+
+    ConfigDescription config;
+    config.density = 320;
+
+    EXPECT_TRUE(filter.match(config));
+}
+
+TEST(WeakResourceFilterTest, MatchesConfigWithSameValueAxis) {
+    WeakResourceFilter filter;
+    ASSERT_EQ(NO_ERROR, filter.parse(String8("fr")));
+
+    ConfigDescription config;
+    config.language[0] = 'f';
+    config.language[1] = 'r';
+
+    EXPECT_TRUE(filter.match(config));
+}
+
+TEST(WeakResourceFilterTest, MatchesConfigWithSameValueAxisAndOtherUnrelatedAxis) {
+    WeakResourceFilter filter;
+    ASSERT_EQ(NO_ERROR, filter.parse(String8("fr")));
+
+    ConfigDescription config;
+    config.language[0] = 'f';
+    config.language[1] = 'r';
+    config.density = 320;
+
+    EXPECT_TRUE(filter.match(config));
+}
+
+TEST(WeakResourceFilterTest, DoesNotMatchConfigWithDifferentValueAxis) {
+    WeakResourceFilter filter;
+    ASSERT_EQ(NO_ERROR, filter.parse(String8("fr")));
+
+    ConfigDescription config;
+    config.language[0] = 'd';
+    config.language[1] = 'e';
+
+    EXPECT_FALSE(filter.match(config));
+}
+
+TEST(WeakResourceFilterTest, MatchesConfigWithSameLanguageButNoRegionSpecified) {
+    WeakResourceFilter filter;
+    ASSERT_EQ(NO_ERROR, filter.parse(String8("de-rDE")));
+
+    ConfigDescription config;
+    config.language[0] = 'd';
+    config.language[1] = 'e';
+
+    EXPECT_TRUE(filter.match(config));
+}
+
+TEST(WeakResourceFilterTest, ParsesStandardLocaleOnlyString) {
+    WeakResourceFilter filter;
+    EXPECT_EQ(NO_ERROR, filter.parse(String8("de_DE")));
+}
+
+TEST(WeakResourceFilterTest, IgnoresVersion) {
+    WeakResourceFilter filter;
+    ASSERT_EQ(NO_ERROR, filter.parse(String8("normal-v4")));
+
+    ConfigDescription config;
+    config.smallestScreenWidthDp = 600;
+    config.version = 13;
+
+    // The configs don't match on any axis besides version, which should be ignored.
+    EXPECT_TRUE(filter.match(config));
+}
+
+TEST(WeakResourceFilterTest, MatchesConfigWithRegion) {
+    WeakResourceFilter filter;
+    ASSERT_EQ(NO_ERROR, filter.parse(String8("kok,kok_IN,kok_419")));
+
+    ConfigDescription config;
+    AaptLocaleValue val;
+    ASSERT_TRUE(val.initFromFilterString(String8("kok_IN")));
+    val.writeTo(&config);
+
+    EXPECT_TRUE(filter.match(config));
+}
+
diff --git a/tools/aapt/tests/TestHelper.h b/tools/aapt/tests/TestHelper.h
new file mode 100644
index 0000000..79174832
--- /dev/null
+++ b/tools/aapt/tests/TestHelper.h
@@ -0,0 +1,33 @@
+/*
+ * 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.
+ */
+
+#ifndef __TEST_HELPER_H
+#define __TEST_HELPER_H
+
+#include <utils/String8.h>
+
+namespace android {
+
+/**
+ * Stream operator for nicely printing String8's in gtest output.
+ */
+inline std::ostream& operator<<(std::ostream& stream, const String8& str) {
+    return stream << str.string();
+}
+
+}
+
+#endif
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 5af1e4e..15b65c1 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -402,12 +402,12 @@
     public static final String EXTRA_LINK_PROPERTIES = "linkProperties";
 
     /**
-     * The lookup key for a {@link android.net.LinkCapabilities} object associated with the
+     * The lookup key for a {@link android.net.NetworkCapabilities} object associated with the
      * Wi-Fi network. Retrieve with
      * {@link android.content.Intent#getParcelableExtra(String)}.
      * @hide
      */
-    public static final String EXTRA_LINK_CAPABILITIES = "linkCapabilities";
+    public static final String EXTRA_NETWORK_CAPABILITIES = "networkCapabilities";
 
     /**
      * The network IDs of the configured networks could have changed.
diff --git a/wifi/java/android/net/wifi/WifiStateTracker.java b/wifi/java/android/net/wifi/WifiStateTracker.java
index 7ded171..40e6649 100644
--- a/wifi/java/android/net/wifi/WifiStateTracker.java
+++ b/wifi/java/android/net/wifi/WifiStateTracker.java
@@ -21,7 +21,7 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.net.BaseNetworkStateTracker;
-import android.net.LinkCapabilities;
+import android.net.NetworkCapabilities;
 import android.net.LinkQualityInfo;
 import android.net.LinkProperties;
 import android.net.NetworkInfo;
@@ -65,7 +65,7 @@
     public WifiStateTracker(int netType, String networkName) {
         mNetworkInfo = new NetworkInfo(netType, 0, networkName, "");
         mLinkProperties = new LinkProperties();
-        mLinkCapabilities = new LinkCapabilities();
+        mNetworkCapabilities = new NetworkCapabilities();
 
         mNetworkInfo.setIsAvailable(false);
         setTeardownRequested(false);
@@ -183,17 +183,6 @@
     }
 
     /**
-     * A capability is an Integer/String pair, the capabilities
-     * are defined in the class LinkSocket#Key.
-     *
-     * @return a copy of this connections capabilities, may be empty but never null.
-     */
-    @Override
-    public LinkCapabilities getLinkCapabilities() {
-        return new LinkCapabilities(mLinkCapabilities);
-    }
-
-    /**
      * Return link info
      * @return an object of type WifiLinkQualityInfo
      */
@@ -264,10 +253,10 @@
                 if (mLinkProperties == null) {
                     mLinkProperties = new LinkProperties();
                 }
-                mLinkCapabilities = intent.getParcelableExtra(
-                        WifiManager.EXTRA_LINK_CAPABILITIES);
-                if (mLinkCapabilities == null) {
-                    mLinkCapabilities = new LinkCapabilities();
+                mNetworkCapabilities = intent.getParcelableExtra(
+                        WifiManager.EXTRA_NETWORK_CAPABILITIES);
+                if (mNetworkCapabilities == null) {
+                    mNetworkCapabilities = new NetworkCapabilities();
                 }
 
                 mWifiInfo = intent.getParcelableExtra(WifiManager.EXTRA_WIFI_INFO);
@@ -323,4 +312,3 @@
         mSamplingDataTracker.stopSampling(s);
     }
 }
-