Merge "Disabling shadowing (until bugs are addressed) and refactoring some constants."
diff --git a/Android.mk b/Android.mk
index 8e283d7..beee726 100644
--- a/Android.mk
+++ b/Android.mk
@@ -199,6 +199,8 @@
 	core/java/android/service/dreams/IDozeHardware.aidl \
 	core/java/android/service/dreams/IDreamManager.aidl \
 	core/java/android/service/dreams/IDreamService.aidl \
+	core/java/android/service/fingerprint/IFingerprintService.aidl \
+	core/java/android/service/fingerprint/IFingerprintServiceReceiver.aidl \
 	core/java/android/service/trust/ITrustAgentService.aidl \
 	core/java/android/service/trust/ITrustAgentServiceCallback.aidl \
 	core/java/android/service/voice/IVoiceInteractionService.aidl \
@@ -718,8 +720,9 @@
 $(full_target): $(framework_built) $(gen)
 
 # Run this for checkbuild
-.PHONY: checkbuild
 checkbuild: doc-comment-check-docs
+# Check comment when you are updating the API
+update-api: doc-comment-check-docs
 
 # ====  static html in the sdk ==================================
 include $(CLEAR_VARS)
diff --git a/api/current.txt b/api/current.txt
index 6153842..2015d00 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -314,6 +314,7 @@
     field public static final int autoCompleteTextViewStyle = 16842859; // 0x101006b
     field public static final int autoLink = 16842928; // 0x10100b0
     field public static final int autoMirrored = 16843754; // 0x10103ea
+    field public static final int autoRemoveFromRecents = 16843859; // 0x1010453
     field public static final int autoStart = 16843445; // 0x10102b5
     field public static final deprecated int autoText = 16843114; // 0x101016a
     field public static final int autoUrlDetect = 16843404; // 0x101028c
@@ -463,6 +464,7 @@
     field public static final int dividerHorizontal = 16843564; // 0x101032c
     field public static final int dividerPadding = 16843562; // 0x101032a
     field public static final int dividerVertical = 16843530; // 0x101030a
+    field public static final int documentLaunchMode = 16843858; // 0x1010452
     field public static final int drawSelectorOnTop = 16843004; // 0x10100fc
     field public static final int drawable = 16843161; // 0x1010199
     field public static final int drawableBottom = 16843118; // 0x101016e
@@ -3361,10 +3363,12 @@
     method public void startIntentSenderForResult(android.content.IntentSender, int, android.content.Intent, int, int, int, android.os.Bundle) throws android.content.IntentSender.SendIntentException;
     method public void startIntentSenderFromChild(android.app.Activity, android.content.IntentSender, int, android.content.Intent, int, int, int) throws android.content.IntentSender.SendIntentException;
     method public void startIntentSenderFromChild(android.app.Activity, android.content.IntentSender, int, android.content.Intent, int, int, int, android.os.Bundle) throws android.content.IntentSender.SendIntentException;
+    method public void startLockTask();
     method public deprecated void startManagingCursor(android.database.Cursor);
     method public boolean startNextMatchingActivity(android.content.Intent);
     method public boolean startNextMatchingActivity(android.content.Intent, android.os.Bundle);
     method public void startSearch(java.lang.String, boolean, android.os.Bundle, boolean);
+    method public void stopLockTask();
     method public deprecated void stopManagingCursor(android.database.Cursor);
     method public void takeKeyEvents(boolean);
     method public void triggerSearch(java.lang.String, android.os.Bundle);
@@ -5013,6 +5017,8 @@
     method public void clearForwardingIntentFilters(android.content.ComponentName);
     method public void clearPackagePersistentPreferredActivities(android.content.ComponentName, java.lang.String);
     method public void clearUserRestriction(android.content.ComponentName, java.lang.String);
+    method public void enableSystemApp(android.content.ComponentName, java.lang.String);
+    method public int enableSystemApp(android.content.ComponentName, android.content.Intent);
     method public java.util.List<android.content.ComponentName> getActiveAdmins();
     method public android.os.Bundle getApplicationRestrictions(android.content.ComponentName, java.lang.String);
     method public boolean getCameraDisabled(android.content.ComponentName);
@@ -6614,6 +6620,7 @@
     field public static final java.lang.String DISPLAY_SERVICE = "display";
     field public static final java.lang.String DOWNLOAD_SERVICE = "download";
     field public static final java.lang.String DROPBOX_SERVICE = "dropbox";
+    field public static final java.lang.String FINGERPRINT_SERVICE = "fingerprint";
     field public static final java.lang.String HDMI_CEC_SERVICE = "hdmi_cec";
     field public static final java.lang.String INPUT_METHOD_SERVICE = "input_method";
     field public static final java.lang.String INPUT_SERVICE = "input";
@@ -7149,6 +7156,7 @@
     field public static final int FILL_IN_PACKAGE = 16; // 0x10
     field public static final int FILL_IN_SELECTOR = 64; // 0x40
     field public static final int FILL_IN_SOURCE_BOUNDS = 32; // 0x20
+    field public static final int FLAG_ACTIVITY_AUTO_REMOVE_FROM_RECENTS = 8192; // 0x2000
     field public static final int FLAG_ACTIVITY_BROUGHT_TO_FRONT = 4194304; // 0x400000
     field public static final int FLAG_ACTIVITY_CLEAR_TASK = 32768; // 0x8000
     field public static final int FLAG_ACTIVITY_CLEAR_TOP = 67108864; // 0x4000000
@@ -7667,8 +7675,12 @@
     field public static final int CONFIG_TOUCHSCREEN = 8; // 0x8
     field public static final int CONFIG_UI_MODE = 512; // 0x200
     field public static final android.os.Parcelable.Creator CREATOR;
+    field public static final int DOCUMENT_LAUNCH_ALWAYS = 2; // 0x2
+    field public static final int DOCUMENT_LAUNCH_INTO_EXISTING = 1; // 0x1
+    field public static final int DOCUMENT_LAUNCH_NONE = 0; // 0x0
     field public static final int FLAG_ALLOW_TASK_REPARENTING = 64; // 0x40
     field public static final int FLAG_ALWAYS_RETAIN_TASK_STATE = 8; // 0x8
+    field public static final int FLAG_AUTO_REMOVE_FROM_RECENTS = 8192; // 0x2000
     field public static final int FLAG_CLEAR_TASK_ON_LAUNCH = 4; // 0x4
     field public static final int FLAG_EXCLUDE_FROM_RECENTS = 32; // 0x20
     field public static final int FLAG_FINISH_ON_CLOSE_SYSTEM_DIALOGS = 256; // 0x100
@@ -7677,6 +7689,7 @@
     field public static final int FLAG_IMMERSIVE = 2048; // 0x800
     field public static final int FLAG_MULTIPROCESS = 1; // 0x1
     field public static final int FLAG_NO_HISTORY = 128; // 0x80
+    field public static final int FLAG_PERSISTABLE = 4096; // 0x1000
     field public static final int FLAG_SINGLE_USER = 1073741824; // 0x40000000
     field public static final int FLAG_STATE_NOT_NEEDED = 16; // 0x10
     field public static final int LAUNCH_MULTIPLE = 0; // 0x0
@@ -7701,6 +7714,7 @@
     field public static final int SCREEN_ORIENTATION_USER_PORTRAIT = 12; // 0xc
     field public static final int UIOPTION_SPLIT_ACTION_BAR_WHEN_NARROW = 1; // 0x1
     field public int configChanges;
+    field public int documentLaunchMode;
     field public int flags;
     field public int launchMode;
     field public java.lang.String parentActivityName;
@@ -10734,6 +10748,7 @@
     method public void releaseTexImage();
     method public void setDefaultBufferSize(int, int);
     method public void setOnFrameAvailableListener(android.graphics.SurfaceTexture.OnFrameAvailableListener);
+    method public void setOnFrameAvailableListener(android.graphics.SurfaceTexture.OnFrameAvailableListener, android.os.Handler);
     method public void updateTexImage();
   }
 
@@ -11838,7 +11853,6 @@
     field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_INFO_WHITE_LEVEL;
     field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_MAX_ANALOG_SENSITIVITY;
     field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_ORIENTATION;
-    field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_PROFILE_HUE_SAT_MAP_DIMENSIONS;
     field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_REFERENCE_ILLUMINANT1;
     field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_REFERENCE_ILLUMINANT2;
     field public static final android.hardware.camera2.CameraMetadata.Key STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES;
@@ -12220,8 +12234,6 @@
     field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_FRAME_DURATION;
     field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_GREEN_SPLIT;
     field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_NEUTRAL_COLOR_POINT;
-    field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_PROFILE_HUE_SAT_MAP;
-    field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_PROFILE_TONE_CURVE;
     field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_SENSITIVITY;
     field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_TEMPERATURE;
     field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_TEST_PATTERN_DATA;
@@ -15462,12 +15474,15 @@
     method public void disconnect(android.media.session.RouteInfo);
     method public android.media.session.SessionToken getSessionToken();
     method public android.media.session.TransportPerformer getTransportPerformer();
-    method public void publish();
+    method public boolean isActive();
     method public void release();
     method public void removeCallback(android.media.session.Session.Callback);
     method public void sendEvent(java.lang.String, android.os.Bundle);
+    method public void setActive(boolean);
+    method public void setFlags(int);
     method public void setRouteOptions(java.util.List<android.media.session.RouteOptions>);
-    method public android.media.session.TransportPerformer setTransportPerformerEnabled();
+    field public static final int FLAG_HANDLES_MEDIA_BUTTONS = 1; // 0x1
+    field public static final int FLAG_HANDLES_TRANSPORT_CONTROLS = 2; // 0x2
   }
 
   public static abstract class Session.Callback {
@@ -15506,7 +15521,7 @@
 
   public final class SessionManager {
     method public android.media.session.Session createSession(java.lang.String);
-    method public java.util.List<android.media.session.SessionController> getActiveSessions();
+    method public java.util.List<android.media.session.SessionController> getActiveSessions(android.content.ComponentName);
   }
 
   public class SessionToken implements android.os.Parcelable {
@@ -15696,6 +15711,7 @@
     method public android.net.NetworkInfo getActiveNetworkInfo();
     method public android.net.NetworkInfo[] getAllNetworkInfo();
     method public deprecated boolean getBackgroundDataSetting();
+    method public android.net.ProxyInfo getGlobalProxy();
     method public android.net.NetworkInfo getNetworkInfo(int);
     method public int getNetworkPreference();
     method public boolean isActiveNetworkMetered();
@@ -15703,6 +15719,7 @@
     method public static boolean isNetworkTypeValid(int);
     method public void registerNetworkActiveListener(android.net.ConnectivityManager.OnNetworkActiveListener);
     method public boolean requestRouteToHost(int, int);
+    method public void setGlobalProxy(android.net.ProxyInfo);
     method public void setNetworkPreference(int);
     method public int startUsingNetworkFeature(int, java.lang.String);
     method public int stopUsingNetworkFeature(int, java.lang.String);
@@ -15878,9 +15895,22 @@
     method public static final deprecated int getDefaultPort();
     method public static final deprecated java.lang.String getHost(android.content.Context);
     method public static final deprecated int getPort(android.content.Context);
+    field public static final java.lang.String EXTRA_PROXY_INFO = "android.intent.extra.PROXY_INFO";
     field public static final java.lang.String PROXY_CHANGE_ACTION = "android.intent.action.PROXY_CHANGE";
   }
 
+  public class ProxyInfo implements android.os.Parcelable {
+    method public static android.net.ProxyInfo buildDirectProxy(java.lang.String, int);
+    method public static android.net.ProxyInfo buildDirectProxy(java.lang.String, int, java.util.List<java.lang.String>);
+    method public static android.net.ProxyInfo buildPacProxy(android.net.Uri);
+    method public int describeContents();
+    method public java.lang.String[] getExclusionList();
+    method public java.lang.String getHost();
+    method public android.net.Uri getPacFileUrl();
+    method public int getPort();
+    method public void writeToParcel(android.os.Parcel, int);
+  }
+
   public class SSLCertificateSocketFactory extends javax.net.ssl.SSLSocketFactory {
     ctor public deprecated SSLCertificateSocketFactory(int);
     method public java.net.Socket createSocket(java.net.Socket, java.lang.String, int, boolean) throws java.io.IOException;
@@ -20504,9 +20534,17 @@
     method public void setUserRestriction(java.lang.String, boolean);
     method public void setUserRestrictions(android.os.Bundle);
     method public void setUserRestrictions(android.os.Bundle, android.os.UserHandle);
+    field public static final java.lang.String DISALLOW_ADD_USER = "no_add_user";
+    field public static final java.lang.String DISALLOW_CONFIG_APPS = "no_config_apps";
     field public static final java.lang.String DISALLOW_CONFIG_BLUETOOTH = "no_config_bluetooth";
+    field public static final java.lang.String DISALLOW_CONFIG_CELL_BROADCASTS = "no_config_cell_broadcasts";
     field public static final java.lang.String DISALLOW_CONFIG_CREDENTIALS = "no_config_credentials";
+    field public static final java.lang.String DISALLOW_CONFIG_MOBILE_NETWORKS = "no_config_mobile_networks";
+    field public static final java.lang.String DISALLOW_CONFIG_TETHERING = "no_config_tethering";
+    field public static final java.lang.String DISALLOW_CONFIG_VPN = "no_config_vpn";
     field public static final java.lang.String DISALLOW_CONFIG_WIFI = "no_config_wifi";
+    field public static final java.lang.String DISALLOW_DEBUGGING_FEATURES = "no_debugging_features";
+    field public static final java.lang.String DISALLOW_FACTORY_RESET = "no_factory_reset";
     field public static final java.lang.String DISALLOW_INSTALL_APPS = "no_install_apps";
     field public static final java.lang.String DISALLOW_INSTALL_UNKNOWN_SOURCES = "no_install_unknown_sources";
     field public static final java.lang.String DISALLOW_MODIFY_ACCOUNTS = "no_modify_accounts";
@@ -20514,6 +20552,7 @@
     field public static final java.lang.String DISALLOW_SHARE_LOCATION = "no_share_location";
     field public static final java.lang.String DISALLOW_UNINSTALL_APPS = "no_uninstall_apps";
     field public static final java.lang.String DISALLOW_USB_FILE_TRANSFER = "no_usb_file_transfer";
+    field public static final java.lang.String ENSURE_VERIFY_APPS = "ensure_verify_apps";
   }
 
   public abstract class Vibrator {
@@ -25058,6 +25097,36 @@
 
 }
 
+package android.service.fingerprint {
+
+  public class FingerprintManager {
+    ctor public FingerprintManager(android.content.Context);
+    method public void enroll(long);
+    method public void remove(int);
+    method public void startListening(android.service.fingerprint.FingerprintManagerReceiver);
+    method public void stopListening();
+    field protected static final boolean DEBUG = true;
+    field public static final int FINGERPRINT_ERROR = -1; // 0xffffffff
+    field public static final int FINGERPRINT_ERROR_BAD_CAPTURE = 2; // 0x2
+    field public static final int FINGERPRINT_ERROR_HW_UNAVAILABLE = 1; // 0x1
+    field public static final int FINGERPRINT_ERROR_NO_RECEIVER = -10; // 0xfffffff6
+    field public static final int FINGERPRINT_ERROR_NO_SPACE = 4; // 0x4
+    field public static final int FINGERPRINT_ERROR_TIMEOUT = 3; // 0x3
+    field public static final int FINGERPRINT_SCANNED = 1; // 0x1
+    field public static final int FINGERPRINT_TEMPLATE_ENROLLING = 2; // 0x2
+    field public static final int FINGERPRINT_TEMPLATE_REMOVED = 4; // 0x4
+  }
+
+  public class FingerprintManagerReceiver {
+    ctor public FingerprintManagerReceiver();
+    method public void onEnrollResult(int, int);
+    method public void onError(int);
+    method public void onRemoved(int);
+    method public void onScanned(int, int);
+  }
+
+}
+
 package android.service.notification {
 
   public abstract class NotificationListenerService extends android.app.Service {
@@ -31595,26 +31664,17 @@
     method public abstract void onFocusLost(android.view.WindowId);
   }
 
-  public class WindowInsets {
+  public final class WindowInsets {
     ctor public WindowInsets(android.view.WindowInsets);
-    method public android.view.WindowInsets cloneWithSystemWindowInsets(int, int, int, int);
-    method public android.view.WindowInsets cloneWithSystemWindowInsetsConsumed();
-    method public android.view.WindowInsets cloneWithSystemWindowInsetsConsumed(boolean, boolean, boolean, boolean);
-    method public android.view.WindowInsets cloneWithWindowDecorInsets(int, int, int, int);
-    method public android.view.WindowInsets cloneWithWindowDecorInsetsConsumed();
-    method public android.view.WindowInsets cloneWithWindowDecorInsetsConsumed(boolean, boolean, boolean, boolean);
+    method public android.view.WindowInsets consumeSystemWindowInsets();
     method public int getSystemWindowInsetBottom();
     method public int getSystemWindowInsetLeft();
     method public int getSystemWindowInsetRight();
     method public int getSystemWindowInsetTop();
-    method public int getWindowDecorInsetBottom();
-    method public int getWindowDecorInsetLeft();
-    method public int getWindowDecorInsetRight();
-    method public int getWindowDecorInsetTop();
     method public boolean hasInsets();
     method public boolean hasSystemWindowInsets();
-    method public boolean hasWindowDecorInsets();
     method public boolean isRound();
+    method public android.view.WindowInsets replaceSystemWindowInsets(int, int, int, int);
   }
 
   public abstract interface WindowManager implements android.view.ViewManager {
@@ -32941,6 +33001,16 @@
     method public boolean hasMimeType(java.lang.String);
   }
 
+  public abstract interface PermissionRequest {
+    method public abstract void deny();
+    method public abstract android.net.Uri getOrigin();
+    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
+  }
+
   public abstract interface PluginStub {
     method public abstract android.view.View getEmbeddedView(int, android.content.Context);
     method public abstract android.view.View getFullScreenView(int, android.content.Context);
@@ -33000,6 +33070,8 @@
     method public boolean onJsConfirm(android.webkit.WebView, java.lang.String, java.lang.String, android.webkit.JsResult);
     method public boolean onJsPrompt(android.webkit.WebView, java.lang.String, java.lang.String, java.lang.String, android.webkit.JsPromptResult);
     method public deprecated boolean onJsTimeout();
+    method public void onPermissionRequest(android.webkit.PermissionRequest);
+    method public void onPermissionRequestCanceled(android.webkit.PermissionRequest);
     method public void onProgressChanged(android.webkit.WebView, int);
     method public deprecated void onReachedMaxAppCacheSize(long, long, android.webkit.WebStorage.QuotaUpdater);
     method public void onReceivedIcon(android.webkit.WebView, android.graphics.Bitmap);
@@ -33287,6 +33359,7 @@
     method public boolean pageUp(boolean);
     method public void pauseTimers();
     method public void postUrl(java.lang.String, byte[]);
+    method public void preauthorizePermission(android.net.Uri, long);
     method public void reload();
     method public void removeJavascriptInterface(java.lang.String);
     method public void requestFocusNodeHref(android.os.Message);
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index af3a92c..4df486a 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -29,6 +29,7 @@
 
 import android.annotation.IntDef;
 import android.annotation.Nullable;
+import android.app.admin.DevicePolicyManager;
 import android.content.ComponentCallbacks2;
 import android.content.ComponentName;
 import android.content.ContentResolver;
@@ -5689,7 +5690,16 @@
         }
     }
 
-    /** @hide */
+    /**
+     * Put this Activity in a mode where the user is locked to the
+     * current task.
+     *
+     * This will prevent the user from launching other apps, going to settings,
+     * or reaching the home screen.
+     *
+     * Lock task mode will only start if the activity has been whitelisted by the
+     * Device Owner through {@link DevicePolicyManager#setLockTaskComponents}.
+     */
     public void startLockTask() {
         try {
             ActivityManagerNative.getDefault().startLockTaskMode(mToken);
@@ -5697,7 +5707,15 @@
         }
     }
 
-    /** @hide */
+    /**
+     * Allow the user to switch away from the current task.
+     *
+     * Called to end the mode started by {@link Activity#startLockTask}. This
+     * can only be called by activities that have successfully called
+     * startLockTask previously.
+     *
+     * This will allow the user to exit this app and move onto other activities.
+     */
     public void stopLockTask() {
         try {
             ActivityManagerNative.getDefault().stopLockTaskMode();
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 3b2ff7f..b606088 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -46,7 +46,7 @@
 import android.hardware.display.DisplayManagerGlobal;
 import android.net.IConnectivityManager;
 import android.net.Proxy;
-import android.net.ProxyProperties;
+import android.net.ProxyInfo;
 import android.opengl.GLUtils;
 import android.os.AsyncTask;
 import android.os.Binder;
@@ -4294,8 +4294,8 @@
             // crash if we can't get it.
             IConnectivityManager service = IConnectivityManager.Stub.asInterface(b);
             try {
-                ProxyProperties proxyProperties = service.getProxy();
-                Proxy.setHttpProxySystemProperty(proxyProperties);
+                ProxyInfo proxyInfo = service.getProxy();
+                Proxy.setHttpProxySystemProperty(proxyInfo);
             } catch (RemoteException e) {}
         }
 
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index fe532bf..c621696 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -106,6 +106,9 @@
 import android.os.storage.StorageManager;
 import android.print.IPrintManager;
 import android.print.PrintManager;
+import android.service.fingerprint.FingerprintManager;
+import android.service.fingerprint.FingerprintManagerReceiver;
+import android.service.fingerprint.FingerprintService;
 import android.telephony.TelephonyManager;
 import android.tv.ITvInputManager;
 import android.tv.TvInputManager;
@@ -451,6 +454,11 @@
                     return new KeyguardManager();
                 }});
 
+        registerService(FINGERPRINT_SERVICE, new ServiceFetcher() {
+            public Object createService(ContextImpl ctx) {
+                return new FingerprintManager(ctx);
+            }});
+
         registerService(LAYOUT_INFLATER_SERVICE, new ServiceFetcher() {
                 public Object createService(ContextImpl ctx) {
                     return PolicyManager.makeNewLayoutInflater(ctx.getOuterContext());
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 929bf65..209c536 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -22,6 +22,7 @@
 import android.annotation.SdkConstant.SdkConstantType;
 import android.content.ComponentName;
 import android.content.Context;
+import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.ActivityInfo;
 import android.content.pm.PackageManager;
@@ -2041,4 +2042,43 @@
             }
         }
     }
+
+    /**
+     * Called by profile or device owner to re-enable a system app that was 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.
+     *
+     * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+     * @param packageName The package to be re-enabled in the current profile.
+     */
+    public void enableSystemApp(ComponentName admin, String packageName) {
+        if (mService != null) {
+            try {
+                mService.enableSystemApp(admin, packageName);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed to install package: " + packageName);
+            }
+        }
+    }
+
+    /**
+     * 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.
+     *
+     * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+     * @param intent An intent matching the app(s) to be installed. All apps that resolve for this
+     *               intent will be re-enabled in the current profile.
+     * @return int The number of activities that matched the intent and were installed.
+     */
+    public int enableSystemApp(ComponentName admin, Intent intent) {
+        if (mService != null) {
+            try {
+                return mService.enableSystemAppWithIntent(admin, intent);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed to install packages matching filter: " + intent);
+            }
+        }
+        return 0;
+    }
 }
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index e3090b6..b30f1b9 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -18,6 +18,7 @@
 package android.app.admin;
 
 import android.content.ComponentName;
+import android.content.Intent;
 import android.content.IntentFilter;
 import android.os.Bundle;
 import android.os.RemoteCallback;
@@ -122,4 +123,7 @@
     void setUserRestriction(in ComponentName who, in String key, boolean enable);
     void addForwardingIntentFilter(in ComponentName admin, in IntentFilter filter, int flags);
     void clearForwardingIntentFilters(in ComponentName admin);
+
+    void enableSystemApp(in ComponentName admin, in String packageName);
+    int enableSystemAppWithIntent(in ComponentName admin, in Intent intent);
 }
diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java
index a396a05..7f8d0ab 100644
--- a/core/java/android/bluetooth/BluetoothDevice.java
+++ b/core/java/android/bluetooth/BluetoothDevice.java
@@ -512,6 +512,25 @@
      */
     public static final String EXTRA_UUID = "android.bluetooth.device.extra.UUID";
 
+     /**
+      * No preferrence of physical transport for GATT connections to remote dual-mode devices
+      * @hide
+      */
+    public static final int TRANSPORT_AUTO = 0;
+
+    /**
+     * Prefer BR/EDR transport for GATT connections to remote dual-mode devices
+     * @hide
+     */
+   public static final int TRANSPORT_BREDR = 1;
+
+    /**
+     * Prefer LE transport for GATT connections to remote dual-mode devices
+     * @hide
+     */
+   public static final int TRANSPORT_LE = 2;
+
+
     /**
      * Lazy initialization. Guaranteed final after first object constructed, or
      * getService() called.
@@ -1216,6 +1235,27 @@
      */
     public BluetoothGatt connectGatt(Context context, boolean autoConnect,
                                      BluetoothGattCallback callback) {
+        return (connectGatt(context, autoConnect,callback, TRANSPORT_AUTO));
+    }
+
+    /**
+     * Connect to GATT Server hosted by this device. Caller acts as GATT client.
+     * The callback is used to deliver results to Caller, such as connection status as well
+     * as any further GATT client operations.
+     * The method returns a BluetoothGatt instance. You can use BluetoothGatt to conduct
+     * GATT client operations.
+     * @param callback GATT callback handler that will receive asynchronous callbacks.
+     * @param autoConnect Whether to directly connect to the remote device (false)
+     *                    or to automatically connect as soon as the remote
+     *                    device becomes available (true).
+     * @param transport preferred transport for GATT connections to remote dual-mode devices
+     *             {@link BluetoothDevice#TRANSPORT_AUTO} or
+     *             {@link BluetoothDevice#TRANSPORT_BREDR} or {@link BluetoothDevice#TRANSPORT_LE}
+     * @throws IllegalArgumentException if callback is null
+     * @hide
+     */
+    public BluetoothGatt connectGatt(Context context, boolean autoConnect,
+                                     BluetoothGattCallback callback, int transport) {
         // TODO(Bluetooth) check whether platform support BLE
         //     Do the check here or in GattServer?
         BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
@@ -1226,10 +1266,11 @@
                 // BLE is not supported
                 return null;
             }
-            BluetoothGatt gatt = new BluetoothGatt(context, iGatt, this);
+            BluetoothGatt gatt = new BluetoothGatt(context, iGatt, this, transport);
             gatt.connect(autoConnect, callback);
             return gatt;
         } catch (RemoteException e) {Log.e(TAG, "", e);}
         return null;
     }
+
 }
diff --git a/core/java/android/bluetooth/BluetoothGatt.java b/core/java/android/bluetooth/BluetoothGatt.java
index ff3af7c..601d9ee 100644
--- a/core/java/android/bluetooth/BluetoothGatt.java
+++ b/core/java/android/bluetooth/BluetoothGatt.java
@@ -51,6 +51,7 @@
     private int mConnState;
     private final Object mStateLock = new Object();
     private Boolean mDeviceBusy = false;
+    private int mTransport;
 
     private static final int CONN_STATE_IDLE = 0;
     private static final int CONN_STATE_CONNECTING = 1;
@@ -135,7 +136,7 @@
                 }
                 try {
                     mService.clientConnect(mClientIf, mDevice.getAddress(),
-                                           !mAutoConnect); // autoConnect is inverse of "isDirect"
+                                           !mAutoConnect, mTransport); // autoConnect is inverse of "isDirect"
                 } catch (RemoteException e) {
                     Log.e(TAG,"",e);
                 }
@@ -600,10 +601,12 @@
             }
         };
 
-    /*package*/ BluetoothGatt(Context context, IBluetoothGatt iGatt, BluetoothDevice device) {
+    /*package*/ BluetoothGatt(Context context, IBluetoothGatt iGatt, BluetoothDevice device,
+                                int transport) {
         mContext = context;
         mService = iGatt;
         mDevice = device;
+        mTransport = transport;
         mServices = new ArrayList<BluetoothGattService>();
 
         mConnState = CONN_STATE_IDLE;
@@ -759,7 +762,7 @@
     public boolean connect() {
         try {
             mService.clientConnect(mClientIf, mDevice.getAddress(),
-                                   false); // autoConnect is inverse of "isDirect"
+                                   false, mTransport); // autoConnect is inverse of "isDirect"
             return true;
         } catch (RemoteException e) {
             Log.e(TAG,"",e);
diff --git a/core/java/android/bluetooth/BluetoothGattServer.java b/core/java/android/bluetooth/BluetoothGattServer.java
index 0c00c06..34e8605 100644
--- a/core/java/android/bluetooth/BluetoothGattServer.java
+++ b/core/java/android/bluetooth/BluetoothGattServer.java
@@ -50,6 +50,7 @@
 
     private Object mServerIfLock = new Object();
     private int mServerIf;
+    private int mTransport;
     private List<BluetoothGattService> mServices;
 
     private static final int CALLBACK_REG_TIMEOUT = 10000;
@@ -269,12 +270,13 @@
     /**
      * Create a BluetoothGattServer proxy object.
      */
-    /*package*/ BluetoothGattServer(Context context, IBluetoothGatt iGatt) {
+    /*package*/ BluetoothGattServer(Context context, IBluetoothGatt iGatt, int transport) {
         mContext = context;
         mService = iGatt;
         mAdapter = BluetoothAdapter.getDefaultAdapter();
         mCallback = null;
         mServerIf = 0;
+        mTransport = transport;
         mServices = new ArrayList<BluetoothGattService>();
     }
 
@@ -401,7 +403,7 @@
 
         try {
             mService.serverConnect(mServerIf, device.getAddress(),
-                               autoConnect ? false : true); // autoConnect is inverse of "isDirect"
+                               autoConnect ? false : true,mTransport); // autoConnect is inverse of "isDirect"
         } catch (RemoteException e) {
             Log.e(TAG,"",e);
             return false;
diff --git a/core/java/android/bluetooth/BluetoothManager.java b/core/java/android/bluetooth/BluetoothManager.java
index 172f3bc..b1618cf3 100644
--- a/core/java/android/bluetooth/BluetoothManager.java
+++ b/core/java/android/bluetooth/BluetoothManager.java
@@ -194,6 +194,26 @@
      */
     public BluetoothGattServer openGattServer(Context context,
                                               BluetoothGattServerCallback callback) {
+
+        return (openGattServer (context, callback, BluetoothDevice.TRANSPORT_AUTO));
+    }
+
+    /**
+     * Open a GATT Server
+     * The callback is used to deliver results to Caller, such as connection status as well
+     * as the results of any other GATT server operations.
+     * The method returns a BluetoothGattServer instance. You can use BluetoothGattServer
+     * to conduct GATT server operations.
+     * @param context App context
+     * @param callback GATT server callback handler that will receive asynchronous callbacks.
+     * @param transport preferred transport for GATT connections to remote dual-mode devices
+     *             {@link BluetoothDevice#TRANSPORT_AUTO} or
+     *             {@link BluetoothDevice#TRANSPORT_BREDR} or {@link BluetoothDevice#TRANSPORT_LE}
+     * @return BluetoothGattServer instance
+     * @hide
+     */
+    public BluetoothGattServer openGattServer(Context context,
+                                              BluetoothGattServerCallback callback,int transport) {
         if (context == null || callback == null) {
             throw new IllegalArgumentException("null parameter: " + context + " " + callback);
         }
@@ -208,7 +228,7 @@
                 Log.e(TAG, "Fail to get GATT Server connection");
                 return null;
             }
-            BluetoothGattServer mGattServer = new BluetoothGattServer(context, iGatt);
+            BluetoothGattServer mGattServer = new BluetoothGattServer(context, iGatt,transport);
             Boolean regStatus = mGattServer.registerCallback(callback);
             return regStatus? mGattServer : null;
         } catch (RemoteException e) {
diff --git a/core/java/android/bluetooth/BluetoothSocket.java b/core/java/android/bluetooth/BluetoothSocket.java
index f532f7c..00fd7ce 100644
--- a/core/java/android/bluetooth/BluetoothSocket.java
+++ b/core/java/android/bluetooth/BluetoothSocket.java
@@ -325,6 +325,7 @@
             }
         } catch (RemoteException e) {
             Log.e(TAG, Log.getStackTraceString(new Throwable()));
+            throw new IOException("unable to send RPC: " + e.getMessage());
         }
     }
 
diff --git a/core/java/android/bluetooth/BluetoothTetheringDataTracker.java b/core/java/android/bluetooth/BluetoothTetheringDataTracker.java
index 6dd551e..a0b603e 100644
--- a/core/java/android/bluetooth/BluetoothTetheringDataTracker.java
+++ b/core/java/android/bluetooth/BluetoothTetheringDataTracker.java
@@ -53,6 +53,9 @@
     private static final boolean DBG = true;
     private static final boolean VDBG = true;
 
+    // Event sent to the mBtdtHandler when DHCP fails so we can tear down the network.
+    private static final int EVENT_NETWORK_FAILED = 1;
+
     private AtomicBoolean mTeardownRequested = new AtomicBoolean(false);
     private AtomicBoolean mPrivateDnsRouteSet = new AtomicBoolean(false);
     private AtomicInteger mDefaultGatewayAddr = new AtomicInteger(0);
@@ -315,6 +318,7 @@
                     }
                     if (!success) {
                         Log.e(TAG, "DHCP request error:" + NetworkUtils.getDhcpError());
+                        mBtdtHandler.obtainMessage(EVENT_NETWORK_FAILED).sendToTarget();
                         return;
                     }
                     mLinkProperties = dhcpResults.linkProperties;
@@ -407,6 +411,10 @@
                     if (VDBG) Log.d(TAG, "got EVENT_NETWORK_DISCONNECTED, " + linkProperties);
                     mBtdt.stopReverseTether();
                     break;
+                case EVENT_NETWORK_FAILED:
+                    if (VDBG) Log.d(TAG, "got EVENT_NETWORK_FAILED");
+                    mBtdt.teardown();
+                    break;
             }
         }
     }
diff --git a/core/java/android/bluetooth/BluetoothUuid.java b/core/java/android/bluetooth/BluetoothUuid.java
index 4b28516..ab53fb0 100644
--- a/core/java/android/bluetooth/BluetoothUuid.java
+++ b/core/java/android/bluetooth/BluetoothUuid.java
@@ -18,6 +18,8 @@
 
 import android.os.ParcelUuid;
 
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
 import java.util.Arrays;
 import java.util.HashSet;
 import java.util.UUID;
@@ -76,6 +78,12 @@
     public static final ParcelUuid BASE_UUID =
             ParcelUuid.fromString("00000000-0000-1000-8000-00805F9B34FB");
 
+    /** Length of bytes for 16 bit UUID */
+    public static final int UUID_BYTES_16_BIT = 2;
+    /** Length of bytes for 32 bit UUID */
+    public static final int UUID_BYTES_32_BIT = 4;
+    /** Length of bytes for 128 bit UUID */
+    public static final int UUID_BYTES_128_BIT = 16;
 
     public static final ParcelUuid[] RESERVED_UUIDS = {
         AudioSink, AudioSource, AdvAudioDist, HSP, Handsfree, AvrcpController, AvrcpTarget,
@@ -216,15 +224,60 @@
     }
 
     /**
+     * Parse UUID from bytes. The {@code uuidBytes} can represent a 16-bit, 32-bit or 128-bit UUID,
+     * but the returned UUID is always in 128-bit format.
+     * Note UUID is little endian in Bluetooth.
+     *
+     * @param uuidBytes Byte representation of uuid.
+     * @return {@link ParcelUuid} parsed from bytes.
+     * @throws IllegalArgumentException If the {@code uuidBytes} cannot be parsed.
+     */
+    public static ParcelUuid parseUuidFrom(byte[] uuidBytes) {
+        if (uuidBytes == null) {
+            throw new IllegalArgumentException("uuidBytes cannot be null");
+        }
+        int length = uuidBytes.length;
+        if (length != UUID_BYTES_16_BIT && length != UUID_BYTES_32_BIT &&
+                length != UUID_BYTES_128_BIT) {
+            throw new IllegalArgumentException("uuidBytes length invalid - " + length);
+        }
+
+        // Construct a 128 bit UUID.
+        if (length == UUID_BYTES_128_BIT) {
+            ByteBuffer buf = ByteBuffer.wrap(uuidBytes).order(ByteOrder.LITTLE_ENDIAN);
+            long msb = buf.getLong(8);
+            long lsb = buf.getLong(0);
+            return new ParcelUuid(new UUID(msb, lsb));
+        }
+
+        // For 16 bit and 32 bit UUID we need to convert them to 128 bit value.
+        // 128_bit_value = uuid * 2^96 + BASE_UUID
+        long shortUuid;
+        if (length == UUID_BYTES_16_BIT) {
+            shortUuid = uuidBytes[0] & 0xFF;
+            shortUuid += (uuidBytes[1] & 0xFF) << 8;
+        } else {
+            shortUuid = uuidBytes[0] & 0xFF ;
+            shortUuid += (uuidBytes[1] & 0xFF) << 8;
+            shortUuid += (uuidBytes[2] & 0xFF) << 16;
+            shortUuid += (uuidBytes[3] & 0xFF) << 24;
+        }
+        long msb = BASE_UUID.getUuid().getMostSignificantBits() + (shortUuid << 32);
+        long lsb = BASE_UUID.getUuid().getLeastSignificantBits();
+        return new ParcelUuid(new UUID(msb, lsb));
+    }
+
+    /**
      * Check whether the given parcelUuid can be converted to 16 bit bluetooth uuid.
+     *
      * @param parcelUuid
      * @return true if the parcelUuid can be converted to 16 bit uuid, false otherwise.
      */
     public static boolean isShortUuid(ParcelUuid parcelUuid) {
-      UUID uuid = parcelUuid.getUuid();
-      if (uuid.getLeastSignificantBits() != BASE_UUID.getUuid().getLeastSignificantBits()) {
-        return false;
-      }
-      return ((uuid.getMostSignificantBits() & 0xFFFF0000FFFFFFFFL) == 0x1000L);
+        UUID uuid = parcelUuid.getUuid();
+        if (uuid.getLeastSignificantBits() != BASE_UUID.getUuid().getLeastSignificantBits()) {
+            return false;
+        }
+        return ((uuid.getMostSignificantBits() & 0xFFFF0000FFFFFFFFL) == 0x1000L);
     }
 }
diff --git a/core/java/android/bluetooth/IBluetoothGatt.aidl b/core/java/android/bluetooth/IBluetoothGatt.aidl
index c6b5c3d..49b156d 100644
--- a/core/java/android/bluetooth/IBluetoothGatt.aidl
+++ b/core/java/android/bluetooth/IBluetoothGatt.aidl
@@ -35,7 +35,7 @@
 
     void registerClient(in ParcelUuid appId, in IBluetoothGattCallback callback);
     void unregisterClient(in int clientIf);
-    void clientConnect(in int clientIf, in String address, in boolean isDirect);
+    void clientConnect(in int clientIf, in String address, in boolean isDirect, in int transport);
     void clientDisconnect(in int clientIf, in String address);
     void startAdvertising(in int appIf);
     void stopAdvertising();
@@ -77,7 +77,7 @@
 
     void registerServer(in ParcelUuid appId, in IBluetoothGattServerCallback callback);
     void unregisterServer(in int serverIf);
-    void serverConnect(in int servertIf, in String address, in boolean isDirect);
+    void serverConnect(in int servertIf, in String address, in boolean isDirect, in int transport);
     void serverDisconnect(in int serverIf, in String address);
     void beginServiceDeclaration(in int serverIf, in int srvcType,
                             in int srvcInstanceId, in int minHandles,
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index de223a3..7c625bd 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -2377,6 +2377,16 @@
 
     /**
      * Use with {@link #getSystemService} to retrieve a
+     * {@link android.service.fingerprint.FingerprintManager} for handling management
+     * of fingerprints.
+     *
+     * @see #getSystemService
+     * @see android.app.FingerprintManager
+     */
+    public static final String FINGERPRINT_SERVICE = "fingerprint";
+
+    /**
+     * Use with {@link #getSystemService} to retrieve a
      * {@link android.media.MediaRouter} for controlling and managing
      * routing of media.
      *
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index ae5437b..3cfc56c 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -3736,7 +3736,8 @@
      *
      * <p>When set, the activity specified by this Intent will launch into a
      * separate task rooted at that activity. The activity launched must be
-     * defined with {@link android.R.attr#launchMode} "standard" or "singleTop".
+     * defined with {@link android.R.attr#launchMode} <code>standard</code>
+     * or <code>singleTop</code>.
      *
      * <p>If FLAG_ACTIVITY_NEW_DOCUMENT is used without
      * {@link #FLAG_ACTIVITY_MULTIPLE_TASK} then the activity manager will
@@ -3751,6 +3752,8 @@
      * always create a new task. Thus the same document may be made to appear
      * more than one time in Recents.
      *
+     * <p>This is equivalent to the attribute {@link android.R.attr#documentLaunchMode}.
+     *
      * @see #FLAG_ACTIVITY_MULTIPLE_TASK
      */
     public static final int FLAG_ACTIVITY_NEW_DOCUMENT =
@@ -3815,6 +3818,15 @@
      */
     public static final int FLAG_ACTIVITY_TASK_ON_HOME = 0X00004000;
     /**
+     * If set and the new activity is the root of a new task, then the task
+     * will remain in the list of recently launched tasks only until all of
+     * the activities in it are finished.
+     *
+     * <p>This is equivalent to the attribute
+     * {@link android.R.styleable#AndroidManifestActivity_autoRemoveFromRecents}.
+     */
+    public static final int FLAG_ACTIVITY_AUTO_REMOVE_FROM_RECENTS = 0x00002000;
+    /**
      * If set, when sending a broadcast only registered receivers will be
      * called -- no BroadcastReceiver components will be launched.
      */
@@ -4019,7 +4031,7 @@
 
     /**
      * Create an intent for a specific component with a specified action and data.
-     * This is equivalent using {@link #Intent(String, android.net.Uri)} to
+     * This is equivalent to using {@link #Intent(String, android.net.Uri)} to
      * construct the Intent and then calling {@link #setClass} to set its
      * class.
      *
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index c53e545..c2fe3a2 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -67,7 +67,37 @@
      * {@link #LAUNCH_SINGLE_INSTANCE}.
      */
     public int launchMode;
-    
+
+    /**
+     * Constant corresponding to <code>none</code> in
+     * the {@link android.R.attr#documentLaunchMode} attribute.
+     */
+    public static final int DOCUMENT_LAUNCH_NONE = 0;
+    /**
+     * Constant corresponding to <code>intoExisting</code> in
+     * the {@link android.R.attr#documentLaunchMode} attribute.
+     */
+    public static final int DOCUMENT_LAUNCH_INTO_EXISTING = 1;
+    /**
+     * Constant corresponding to <code>always</code> in
+     * the {@link android.R.attr#documentLaunchMode} attribute.
+     */
+    public static final int DOCUMENT_LAUNCH_ALWAYS = 2;
+    /**
+     * The document launch mode style requested by the activity. From the
+     * {@link android.R.attr#documentLaunchMode} attribute, one of
+     * {@link #DOCUMENT_LAUNCH_NONE}, {@link #DOCUMENT_LAUNCH_INTO_EXISTING},
+     * {@link #DOCUMENT_LAUNCH_ALWAYS}.
+     *
+     * <p>Modes DOCUMENT_LAUNCH_ALWAYS
+     * and DOCUMENT_LAUNCH_INTO_EXISTING are equivalent to {@link
+     * android.content.Intent#FLAG_ACTIVITY_NEW_DOCUMENT
+     * Intent.FLAG_ACTIVITY_NEW_DOCUMENT} with and without {@link
+     * android.content.Intent#FLAG_ACTIVITY_MULTIPLE_TASK
+     * Intent.FLAG_ACTIVITY_MULTIPLE_TASK} respectively.
+     */
+    public int documentLaunchMode;
+
     /**
      * Optional name of a permission required to be able to access this
      * Activity.  From the "permission" attribute.
@@ -192,10 +222,15 @@
      * Bit in {@link #flags} indicating that this activity is to be persisted across
      * reboots for display in the Recents list.
      * {@link android.R.attr#persistable}
-     * @hide
      */
     public static final int FLAG_PERSISTABLE = 0x1000;
     /**
+     * Bit in {@link #flags} indicating that tasks started with this activity are to be
+     * removed from the recent list of tasks when the last activity in the task is finished.
+     * {@link android.R.attr#autoRemoveFromRecents}
+     */
+    public static final int FLAG_AUTO_REMOVE_FROM_RECENTS = 0x2000;
+    /**
      * @hide Bit in {@link #flags}: If set, this component will only be seen
      * by the primary user.  Only works with broadcast receivers.  Set from the
      * android.R.attr#primaryUserOnly attribute.
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 080b37b..d80ab7b 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -2462,17 +2462,6 @@
             a.info.flags |= ActivityInfo.FLAG_IMMERSIVE;
         }
 
-        if (sa.getBoolean(
-                com.android.internal.R.styleable.AndroidManifestActivity_persistable, false)) {
-            a.info.flags |= ActivityInfo.FLAG_PERSISTABLE;
-        }
-
-        if (sa.getBoolean(
-                com.android.internal.R.styleable.AndroidManifestActivity_allowEmbedded,
-                false)) {
-            a.info.flags |= ActivityInfo.FLAG_ALLOW_EMBEDDED;
-        }
-
         if (!receiver) {
             if (sa.getBoolean(
                     com.android.internal.R.styleable.AndroidManifestActivity_hardwareAccelerated,
@@ -2483,6 +2472,9 @@
             a.info.launchMode = sa.getInt(
                     com.android.internal.R.styleable.AndroidManifestActivity_launchMode,
                     ActivityInfo.LAUNCH_MULTIPLE);
+            a.info.documentLaunchMode = sa.getInt(
+                    com.android.internal.R.styleable.AndroidManifestActivity_documentLaunchMode,
+                    ActivityInfo.DOCUMENT_LAUNCH_NONE);
             a.info.screenOrientation = sa.getInt(
                     com.android.internal.R.styleable.AndroidManifestActivity_screenOrientation,
                     ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
@@ -2492,6 +2484,23 @@
             a.info.softInputMode = sa.getInt(
                     com.android.internal.R.styleable.AndroidManifestActivity_windowSoftInputMode,
                     0);
+
+            if (sa.getBoolean(
+                    com.android.internal.R.styleable.AndroidManifestActivity_persistable, false)) {
+                a.info.flags |= ActivityInfo.FLAG_PERSISTABLE;
+            }
+
+            if (sa.getBoolean(
+                    com.android.internal.R.styleable.AndroidManifestActivity_allowEmbedded,
+                    false)) {
+                a.info.flags |= ActivityInfo.FLAG_ALLOW_EMBEDDED;
+            }
+
+            if (sa.getBoolean(
+                    com.android.internal.R.styleable.AndroidManifestActivity_autoRemoveFromRecents,
+                    false)) {
+                a.info.flags |= ActivityInfo.FLAG_AUTO_REMOVE_FROM_RECENTS;
+            }
         } else {
             a.info.launchMode = ActivityInfo.LAUNCH_MULTIPLE;
             a.info.configChanges = 0;
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index 28309d7..5f2af8cf 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -1277,20 +1277,6 @@
             new Key<Integer>("android.sensor.orientation", int.class);
 
     /**
-     * <p>The number of input samples for each dimension of
-     * {@link CaptureResult#SENSOR_PROFILE_HUE_SAT_MAP android.sensor.profileHueSatMap}.</p>
-     * <p>The number of input samples for the hue, saturation, and value
-     * dimension of {@link CaptureResult#SENSOR_PROFILE_HUE_SAT_MAP android.sensor.profileHueSatMap}. The order of the
-     * dimensions given is hue, saturation, value; where hue is the 0th
-     * element.</p>
-     * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
-     *
-     * @see CaptureResult#SENSOR_PROFILE_HUE_SAT_MAP
-     */
-    public static final Key<int[]> SENSOR_PROFILE_HUE_SAT_MAP_DIMENSIONS =
-            new Key<int[]>("android.sensor.profileHueSatMapDimensions", int[].class);
-
-    /**
      * <p>Optional. Defaults to [OFF]. Lists the supported test
      * pattern modes for {@link CaptureRequest#SENSOR_TEST_PATTERN_MODE android.sensor.testPatternMode}.</p>
      * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index 1d2d0e9..51ea447 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -1906,36 +1906,6 @@
             new Key<Rational[]>("android.sensor.neutralColorPoint", Rational[].class);
 
     /**
-     * <p>A mapping containing a hue shift, saturation scale, and value scale
-     * for each pixel.</p>
-     * <p>hue_samples, saturation_samples, and value_samples are given in
-     * {@link CameraCharacteristics#SENSOR_PROFILE_HUE_SAT_MAP_DIMENSIONS android.sensor.profileHueSatMapDimensions}.</p>
-     * <p>Each entry of this map contains three floats corresponding to the
-     * hue shift, saturation scale, and value scale, respectively; where the
-     * hue shift has the lowest index. The map entries are stored in the tag
-     * in nested loop order, with the value divisions in the outer loop, the
-     * hue divisions in the middle loop, and the saturation divisions in the
-     * inner loop. All zero input saturation entries are required to have a
-     * value scale factor of 1.0.</p>
-     * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
-     *
-     * @see CameraCharacteristics#SENSOR_PROFILE_HUE_SAT_MAP_DIMENSIONS
-     */
-    public static final Key<float[]> SENSOR_PROFILE_HUE_SAT_MAP =
-            new Key<float[]>("android.sensor.profileHueSatMap", float[].class);
-
-    /**
-     * <p>A list of x,y samples defining a tone-mapping curve for gamma adjustment.</p>
-     * <p>This tag contains a default tone curve that can be applied while
-     * processing the image as a starting point for user adjustments.
-     * The curve is specified as a list of value pairs in linear gamma.
-     * The curve is interpolated using a cubic spline.</p>
-     * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
-     */
-    public static final Key<float[]> SENSOR_PROFILE_TONE_CURVE =
-            new Key<float[]>("android.sensor.profileToneCurve", float[].class);
-
-    /**
      * <p>The worst-case divergence between Bayer green channels.</p>
      * <p>This value is an estimate of the worst case split between the
      * Bayer green channels in the red and blue rows in the sensor color
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 3da00b1..25708ea 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -1307,14 +1307,13 @@
      * doing something unusual like general internal filtering this may be useful.  On
      * a private network where the proxy is not accessible, you may break HTTP using this.
      *
-     * @param p The a {@link ProxyProperties} object defining the new global
+     * @param p The a {@link ProxyInfo} object defining the new global
      *        HTTP proxy.  A {@code null} value will clear the global HTTP proxy.
      *
      * <p>This method requires the call to hold the permission
      * {@link android.Manifest.permission#CONNECTIVITY_INTERNAL}.
-     * {@hide}
      */
-    public void setGlobalProxy(ProxyProperties p) {
+    public void setGlobalProxy(ProxyInfo p) {
         try {
             mService.setGlobalProxy(p);
         } catch (RemoteException e) {
@@ -1324,14 +1323,13 @@
     /**
      * Retrieve any network-independent global HTTP proxy.
      *
-     * @return {@link ProxyProperties} for the current global HTTP proxy or {@code null}
+     * @return {@link ProxyInfo} for the current global HTTP proxy or {@code null}
      *        if no global HTTP proxy is set.
      *
      * <p>This method requires the call to hold the permission
      * {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
-     * {@hide}
      */
-    public ProxyProperties getGlobalProxy() {
+    public ProxyInfo getGlobalProxy() {
         try {
             return mService.getGlobalProxy();
         } catch (RemoteException e) {
@@ -1343,14 +1341,14 @@
      * Get the HTTP proxy settings for the current default network.  Note that
      * if a global proxy is set, it will override any per-network setting.
      *
-     * @return the {@link ProxyProperties} for the current HTTP proxy, or {@code null} if no
+     * @return the {@link ProxyInfo} for the current HTTP proxy, or {@code null} if no
      *        HTTP proxy is active.
      *
      * <p>This method requires the call to hold the permission
      * {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
      * {@hide}
      */
-    public ProxyProperties getProxy() {
+    public ProxyInfo getProxy() {
         try {
             return mService.getProxy();
         } catch (RemoteException e) {
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index 381a817..d53a856 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -21,7 +21,7 @@
 import android.net.NetworkInfo;
 import android.net.NetworkQuotaInfo;
 import android.net.NetworkState;
-import android.net.ProxyProperties;
+import android.net.ProxyInfo;
 import android.os.IBinder;
 import android.os.Messenger;
 import android.os.ParcelFileDescriptor;
@@ -107,11 +107,11 @@
 
     void reportInetCondition(int networkType, int percentage);
 
-    ProxyProperties getGlobalProxy();
+    ProxyInfo getGlobalProxy();
 
-    void setGlobalProxy(in ProxyProperties p);
+    void setGlobalProxy(in ProxyInfo p);
 
-    ProxyProperties getProxy();
+    ProxyInfo getProxy();
 
     void setDataDependency(int networkType, boolean met);
 
diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java
index 4dfd3d9..2dcc544 100644
--- a/core/java/android/net/LinkProperties.java
+++ b/core/java/android/net/LinkProperties.java
@@ -16,7 +16,7 @@
 
 package android.net;
 
-import android.net.ProxyProperties;
+import android.net.ProxyInfo;
 import android.os.Parcelable;
 import android.os.Parcel;
 import android.text.TextUtils;
@@ -65,7 +65,7 @@
     private ArrayList<InetAddress> mDnses = new ArrayList<InetAddress>();
     private String mDomains;
     private ArrayList<RouteInfo> mRoutes = new ArrayList<RouteInfo>();
-    private ProxyProperties mHttpProxy;
+    private ProxyInfo mHttpProxy;
     private int mMtu;
 
     // Stores the properties of links that are "stacked" above this link.
@@ -101,7 +101,7 @@
             mDomains = source.getDomains();
             for (RouteInfo r : source.getRoutes()) mRoutes.add(r);
             mHttpProxy = (source.getHttpProxy() == null)  ?
-                    null : new ProxyProperties(source.getHttpProxy());
+                    null : new ProxyInfo(source.getHttpProxy());
             for (LinkProperties l: source.mStackedLinks.values()) {
                 addStackedLink(l);
             }
@@ -295,10 +295,10 @@
         return routes;
     }
 
-    public void setHttpProxy(ProxyProperties proxy) {
+    public void setHttpProxy(ProxyInfo proxy) {
         mHttpProxy = proxy;
     }
-    public ProxyProperties getHttpProxy() {
+    public ProxyInfo getHttpProxy() {
         return mHttpProxy;
     }
 
@@ -720,7 +720,7 @@
                     netProp.addRoute((RouteInfo)in.readParcelable(null));
                 }
                 if (in.readByte() == 1) {
-                    netProp.setHttpProxy((ProxyProperties)in.readParcelable(null));
+                    netProp.setHttpProxy((ProxyInfo)in.readParcelable(null));
                 }
                 ArrayList<LinkProperties> stackedLinks = new ArrayList<LinkProperties>();
                 in.readList(stackedLinks, LinkProperties.class.getClassLoader());
diff --git a/core/java/android/net/Proxy.java b/core/java/android/net/Proxy.java
index bea8d1c..8f41e85 100644
--- a/core/java/android/net/Proxy.java
+++ b/core/java/android/net/Proxy.java
@@ -19,6 +19,7 @@
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
 import android.content.Context;
+import android.net.ProxyInfo;
 import android.text.TextUtils;
 import android.util.Log;
 
@@ -63,8 +64,11 @@
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     public static final String PROXY_CHANGE_ACTION = "android.intent.action.PROXY_CHANGE";
-    /** {@hide} **/
-    public static final String EXTRA_PROXY_INFO = "proxy";
+    /**
+     * Intent extra included with {@link #PROXY_CHANGE_ACTION} intents.
+     * It describes the new proxy being used (as a {@link ProxyInfo} object).
+     */
+    public static final String EXTRA_PROXY_INFO = "android.intent.extra.PROXY_INFO";
 
     /** @hide */
     public static final int PROXY_VALID             = 0;
@@ -114,24 +118,14 @@
      */
     public static final java.net.Proxy getProxy(Context ctx, String url) {
         String host = "";
-        if (url != null) {
+        if ((url != null) && !isLocalHost(host)) {
             URI uri = URI.create(url);
-            host = uri.getHost();
-        }
+            ProxySelector proxySelector = ProxySelector.getDefault();
 
-        if (!isLocalHost(host)) {
-            if (sConnectivityManager == null) {
-                sConnectivityManager = (ConnectivityManager)ctx.getSystemService(
-                        Context.CONNECTIVITY_SERVICE);
-            }
-            if (sConnectivityManager == null) return java.net.Proxy.NO_PROXY;
+            List<java.net.Proxy> proxyList = proxySelector.select(uri);
 
-            ProxyProperties proxyProperties = sConnectivityManager.getProxy();
-
-            if (proxyProperties != null) {
-                if (!proxyProperties.isExcluded(host)) {
-                    return proxyProperties.makeProxy();
-                }
+            if (proxyList.size() > 0) {
+                return proxyList.get(0);
             }
         }
         return java.net.Proxy.NO_PROXY;
@@ -275,7 +269,7 @@
     }
 
     /** @hide */
-    public static final void setHttpProxySystemProperty(ProxyProperties p) {
+    public static final void setHttpProxySystemProperty(ProxyInfo p) {
         String host = null;
         String port = null;
         String exclList = null;
@@ -283,8 +277,8 @@
         if (p != null) {
             host = p.getHost();
             port = Integer.toString(p.getPort());
-            exclList = p.getExclusionList();
-            pacFileUrl = p.getPacFileUrl();
+            exclList = p.getExclusionListAsString();
+            pacFileUrl = p.getPacFileUrl().toString();
         }
         setHttpProxySystemProperty(host, port, exclList, pacFileUrl);
     }
diff --git a/core/java/android/net/ProxyProperties.aidl b/core/java/android/net/ProxyInfo.aidl
similarity index 95%
rename from core/java/android/net/ProxyProperties.aidl
rename to core/java/android/net/ProxyInfo.aidl
index 02ea15d..2c91960 100644
--- a/core/java/android/net/ProxyProperties.aidl
+++ b/core/java/android/net/ProxyInfo.aidl
@@ -17,5 +17,5 @@
 
 package android.net;
 
-parcelable ProxyProperties;
+parcelable ProxyInfo;
 
diff --git a/core/java/android/net/ProxyProperties.java b/core/java/android/net/ProxyInfo.java
similarity index 61%
rename from core/java/android/net/ProxyProperties.java
rename to core/java/android/net/ProxyInfo.java
index 50f45e8..b40941f 100644
--- a/core/java/android/net/ProxyProperties.java
+++ b/core/java/android/net/ProxyInfo.java
@@ -21,14 +21,23 @@
 import android.os.Parcelable;
 import android.text.TextUtils;
 
+import org.apache.http.client.HttpClient;
+
 import java.net.InetSocketAddress;
+import java.net.URLConnection;
+import java.util.List;
 import java.util.Locale;
 
 /**
- * A container class for the http proxy info
- * @hide
+ * Describes a proxy configuration.
+ *
+ * Proxy configurations are already integrated within the Apache HTTP stack.
+ * So {@link URLConnection} and {@link HttpClient} will use them automatically.
+ *
+ * Other HTTP stacks will need to obtain the proxy info from
+ * {@link Proxy#PROXY_CHANGE_ACTION} broadcast as the extra {@link Proxy#EXTRA_PROXY_INFO}.
  */
-public class ProxyProperties implements Parcelable {
+public class ProxyInfo implements Parcelable {
 
     private String mHost;
     private int mPort;
@@ -36,32 +45,82 @@
     private String[] mParsedExclusionList;
 
     private String mPacFileUrl;
+    /**
+     *@hide
+     */
     public static final String LOCAL_EXCL_LIST = "";
+    /**
+     *@hide
+     */
     public static final int LOCAL_PORT = -1;
+    /**
+     *@hide
+     */
     public static final String LOCAL_HOST = "localhost";
 
-    public ProxyProperties(String host, int port, String exclList) {
+    /**
+     * Constructs a {@link ProxyInfo} object that points at a Direct proxy
+     * on the specified host and port.
+     */
+    public static ProxyInfo buildDirectProxy(String host, int port) {
+        return new ProxyInfo(host, port, null);
+    }
+
+    /**
+     * Constructs a {@link ProxyInfo} object that points at a Direct proxy
+     * on the specified host and port.
+     *
+     * The proxy will not be used to access any host in exclusion list, exclList.
+     *
+     * @param exclList Hosts to exclude using the proxy on connections for.  These
+     *                 hosts can use wildcards such as *.example.com.
+     */
+    public static ProxyInfo buildDirectProxy(String host, int port, List<String> exclList) {
+        String[] array = exclList.toArray(new String[exclList.size()]);
+        return new ProxyInfo(host, port, TextUtils.join(",", array), array);
+    }
+
+    /**
+     * Construct a {@link ProxyInfo} that will download and run the PAC script
+     * at the specified URL.
+     */
+    public static ProxyInfo buildPacProxy(Uri pacUri) {
+        return new ProxyInfo(pacUri.toString());
+    }
+
+    /**
+     * Create a ProxyProperties that points at a HTTP Proxy.
+     * @hide
+     */
+    public ProxyInfo(String host, int port, String exclList) {
         mHost = host;
         mPort = port;
         setExclusionList(exclList);
     }
 
-    public ProxyProperties(String pacFileUrl) {
+    /**
+     * Create a ProxyProperties that points at a PAC URL.
+     * @hide
+     */
+    public ProxyInfo(String pacFileUrl) {
         mHost = LOCAL_HOST;
         mPort = LOCAL_PORT;
         setExclusionList(LOCAL_EXCL_LIST);
         mPacFileUrl = pacFileUrl;
     }
 
-    // Only used in PacManager after Local Proxy is bound.
-    public ProxyProperties(String pacFileUrl, int localProxyPort) {
+    /**
+     * Only used in PacManager after Local Proxy is bound.
+     * @hide
+     */
+    public ProxyInfo(String pacFileUrl, int localProxyPort) {
         mHost = LOCAL_HOST;
         mPort = localProxyPort;
         setExclusionList(LOCAL_EXCL_LIST);
         mPacFileUrl = pacFileUrl;
     }
 
-    private ProxyProperties(String host, int port, String exclList, String[] parsedExclList) {
+    private ProxyInfo(String host, int port, String exclList, String[] parsedExclList) {
         mHost = host;
         mPort = port;
         mExclusionList = exclList;
@@ -70,16 +129,22 @@
     }
 
     // copy constructor instead of clone
-    public ProxyProperties(ProxyProperties source) {
+    /**
+     * @hide
+     */
+    public ProxyInfo(ProxyInfo source) {
         if (source != null) {
             mHost = source.getHost();
             mPort = source.getPort();
-            mPacFileUrl = source.getPacFileUrl();
-            mExclusionList = source.getExclusionList();
+            mPacFileUrl = source.mPacFileUrl;
+            mExclusionList = source.getExclusionListAsString();
             mParsedExclusionList = source.mParsedExclusionList;
         }
     }
 
+    /**
+     * @hide
+     */
     public InetSocketAddress getSocketAddress() {
         InetSocketAddress inetSocketAddress = null;
         try {
@@ -88,20 +153,46 @@
         return inetSocketAddress;
     }
 
-    public String getPacFileUrl() {
-        return mPacFileUrl;
+    /**
+     * Returns the URL of the current PAC script or null if there is
+     * no PAC script.
+     */
+    public Uri getPacFileUrl() {
+        if (TextUtils.isEmpty(mPacFileUrl)) {
+            return null;
+        }
+        return Uri.parse(mPacFileUrl);
     }
 
+    /**
+     * When configured to use a Direct Proxy this returns the host
+     * of the proxy.
+     */
     public String getHost() {
         return mHost;
     }
 
+    /**
+     * When configured to use a Direct Proxy this returns the port
+     * of the proxy
+     */
     public int getPort() {
         return mPort;
     }
 
-    // comma separated
-    public String getExclusionList() {
+    /**
+     * When configured to use a Direct Proxy this returns the list
+     * of hosts for which the proxy is ignored.
+     */
+    public String[] getExclusionList() {
+        return mParsedExclusionList;
+    }
+
+    /**
+     * comma separated
+     * @hide
+     */
+    public String getExclusionListAsString() {
         return mExclusionList;
     }
 
@@ -111,33 +202,13 @@
         if (mExclusionList == null) {
             mParsedExclusionList = new String[0];
         } else {
-            String splitExclusionList[] = exclusionList.toLowerCase(Locale.ROOT).split(",");
-            mParsedExclusionList = new String[splitExclusionList.length * 2];
-            for (int i = 0; i < splitExclusionList.length; i++) {
-                String s = splitExclusionList[i].trim();
-                if (s.startsWith(".")) s = s.substring(1);
-                mParsedExclusionList[i*2] = s;
-                mParsedExclusionList[(i*2)+1] = "." + s;
-            }
+            mParsedExclusionList = exclusionList.toLowerCase(Locale.ROOT).split(",");
         }
     }
 
-    public boolean isExcluded(String url) {
-        if (TextUtils.isEmpty(url) || mParsedExclusionList == null ||
-                mParsedExclusionList.length == 0) return false;
-
-        Uri u = Uri.parse(url);
-        String urlDomain = u.getHost();
-        if (urlDomain == null) return false;
-        for (int i = 0; i< mParsedExclusionList.length; i+=2) {
-            if (urlDomain.equals(mParsedExclusionList[i]) ||
-                    urlDomain.endsWith(mParsedExclusionList[i+1])) {
-                return true;
-            }
-        }
-        return false;
-    }
-
+    /**
+     * @hide
+     */
     public boolean isValid() {
         if (!TextUtils.isEmpty(mPacFileUrl)) return true;
         return Proxy.PROXY_VALID == Proxy.validate(mHost == null ? "" : mHost,
@@ -145,6 +216,9 @@
                                                 mExclusionList == null ? "" : mExclusionList);
     }
 
+    /**
+     * @hide
+     */
     public java.net.Proxy makeProxy() {
         java.net.Proxy proxy = java.net.Proxy.NO_PROXY;
         if (mHost != null) {
@@ -179,17 +253,17 @@
 
     @Override
     public boolean equals(Object o) {
-        if (!(o instanceof ProxyProperties)) return false;
-        ProxyProperties p = (ProxyProperties)o;
+        if (!(o instanceof ProxyInfo)) return false;
+        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)) {
             return mPacFileUrl.equals(p.getPacFileUrl()) && mPort == p.mPort;
         }
-        if (!TextUtils.isEmpty(p.getPacFileUrl())) {
+        if (!TextUtils.isEmpty(p.mPacFileUrl)) {
             return false;
         }
-        if (mExclusionList != null && !mExclusionList.equals(p.getExclusionList())) return false;
+        if (mExclusionList != null && !mExclusionList.equals(p.getExclusionListAsString())) return false;
         if (mHost != null && p.getHost() != null && mHost.equals(p.getHost()) == false) {
             return false;
         }
@@ -245,15 +319,15 @@
      * Implement the Parcelable interface.
      * @hide
      */
-    public static final Creator<ProxyProperties> CREATOR =
-        new Creator<ProxyProperties>() {
-            public ProxyProperties createFromParcel(Parcel in) {
+    public static final Creator<ProxyInfo> CREATOR =
+        new Creator<ProxyInfo>() {
+            public ProxyInfo createFromParcel(Parcel in) {
                 String host = null;
                 int port = 0;
                 if (in.readByte() != 0) {
                     String url = in.readString();
                     int localPort = in.readInt();
-                    return new ProxyProperties(url, localPort);
+                    return new ProxyInfo(url, localPort);
                 }
                 if (in.readByte() != 0) {
                     host = in.readString();
@@ -261,13 +335,13 @@
                 }
                 String exclList = in.readString();
                 String[] parsedExclList = in.readStringArray();
-                ProxyProperties proxyProperties =
-                        new ProxyProperties(host, port, exclList, parsedExclList);
+                ProxyInfo proxyProperties =
+                        new ProxyInfo(host, port, exclList, parsedExclList);
                 return proxyProperties;
             }
 
-            public ProxyProperties[] newArray(int size) {
-                return new ProxyProperties[size];
+            public ProxyInfo[] newArray(int size) {
+                return new ProxyInfo[size];
             }
         };
 }
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 1b2b798..84639eb 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -91,7 +91,6 @@
      * @see #setUserRestrictions(Bundle)
      * @see #getUserRestrictions()
      */
-
     public static final String DISALLOW_SHARE_LOCATION = "no_share_location";
 
     /**
@@ -145,6 +144,96 @@
      */
     public static final String DISALLOW_REMOVE_USER = "no_remove_user";
 
+    /**
+     * Key for user restrictions. Specifies if a user is disallowed from enabling or
+     * accessing debugging features. The default value is <code>false</code>.
+     * <p/>
+     * Type: Boolean
+     * @see #setUserRestrictions(Bundle)
+     * @see #getUserRestrictions()
+     */
+    public static final String DISALLOW_DEBUGGING_FEATURES = "no_debugging_features";
+
+    /**
+     * Key for user restrictions. Specifies if a user is disallowed from configuring VPN.
+     * The default value is <code>false</code>.
+     * <p/>
+     * Type: Boolean
+     * @see #setUserRestrictions(Bundle)
+     * @see #getUserRestrictions()
+     */
+    public static final String DISALLOW_CONFIG_VPN = "no_config_vpn";
+
+    /**
+     * Key for user restrictions. Specifies if a user is disallowed from configuring Tethering
+     * & portable hotspots. The default value is <code>false</code>.
+     * <p/>
+     * Type: Boolean
+     * @see #setUserRestrictions(Bundle)
+     * @see #getUserRestrictions()
+     */
+    public static final String DISALLOW_CONFIG_TETHERING = "no_config_tethering";
+
+    /**
+     * Key for user restrictions. Specifies if a user is disallowed from factory resetting
+     * from Settings.
+     * The default value is <code>false</code>.
+     * <p>
+     * @see #setUserRestrictions(Bundle)
+     * @see #getUserRestrictions()
+     */
+    public static final String DISALLOW_FACTORY_RESET = "no_factory_reset";
+
+    /**
+     * Key for user restrictions. Specifies if a user is disallowed from adding new users and
+     * profiles. The default value is <code>false</code>.
+     * <p>
+     * Type: Boolean
+     * @see #setUserRestrictions(Bundle)
+     * @see #getUserRestrictions()
+     */
+    public static final String DISALLOW_ADD_USER = "no_add_user";
+
+    /**
+     * Key for user restrictions. Specifies if a user is disallowed from disabling application
+     * verification. The default value is <code>false</code>.
+     * <p>
+     * Type: Boolean
+     * @see #setUserRestrictions(Bundle)
+     * @see #getUserRestrictions()
+     */
+    public static final String ENSURE_VERIFY_APPS = "ensure_verify_apps";
+
+    /**
+     * Key for user restrictions. Specifies if a user is disallowed from configuring cell
+     * broadcasts. The default value is <code>false</code>.
+     * <p>
+     * Type: Boolean
+     * @see #setUserRestrictions(Bundle)
+     * @see #getUserRestrictions()
+     */
+    public static final String DISALLOW_CONFIG_CELL_BROADCASTS = "no_config_cell_broadcasts";
+
+    /**
+     * Key for user restrictions. Specifies if a user is disallowed from configuring mobile
+     * networks. The default value is <code>false</code>.
+     * <p>
+     * Type: Boolean
+     * @see #setUserRestrictions(Bundle)
+     * @see #getUserRestrictions()
+     */
+    public static final String DISALLOW_CONFIG_MOBILE_NETWORKS = "no_config_mobile_networks";
+
+    /**
+     * Key for user restrictions. Specifies if a user is disallowed from configuring
+     * applications in Settings. The default value is <code>false</code>.
+     * <p>
+     * Type: Boolean
+     * @see #setUserRestrictions(Bundle)
+     * @see #getUserRestrictions()
+     */
+    public static final String DISALLOW_CONFIG_APPS = "no_config_apps";
+
     /** @hide */
     public static final int PIN_VERIFICATION_FAILED_INCORRECT = -3;
     /** @hide */
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 0eb994d..d5a3bcb 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -3480,6 +3480,12 @@
             "lock_screen_appwidget_ids";
 
         /**
+         * List of enrolled fingerprint identifiers (comma-delimited).
+         * @hide
+         */
+        public static final String USER_FINGERPRINT_IDS = "user_fingerprint_ids";
+
+        /**
          * Id of the appwidget shown on the lock screen when appwidgets are disabled.
          * @hide
          */
diff --git a/core/java/android/service/fingerprint/FingerprintManager.java b/core/java/android/service/fingerprint/FingerprintManager.java
new file mode 100644
index 0000000..0d14c59
--- /dev/null
+++ b/core/java/android/service/fingerprint/FingerprintManager.java
@@ -0,0 +1,200 @@
+/**
+ * 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.fingerprint;
+
+import android.app.ActivityManagerNative;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.util.Log;
+
+/**
+ * A class that coordinates access to the fingerprint hardware.
+ */
+
+public class FingerprintManager {
+    private static final String TAG = "FingerprintManager";
+    protected static final boolean DEBUG = true;
+    private static final String FINGERPRINT_SERVICE_PACKAGE = "com.android.service.fingerprint";
+    private static final String FINGERPRINT_SERVICE_CLASS =
+            "com.android.service.fingerprint.FingerprintService";
+    private static final int MSG_ENROLL_RESULT = 100;
+    private static final int MSG_SCANNED = 101;
+    private static final int MSG_ERROR = 102;
+    private static final int MSG_REMOVED = 103;
+
+    public static final int FINGERPRINT_ERROR_NO_RECEIVER = -10;
+    public static final int FINGERPRINT_ERROR = -1; // One of the error messages below.
+
+    // Progress messages.
+    public static final int FINGERPRINT_SCANNED = 1;
+    public static final int FINGERPRINT_TEMPLATE_ENROLLING = 2;
+    public static final int FINGERPRINT_TEMPLATE_REMOVED = 4;
+
+    // Error messages. Must agree with fingerprint HAL definitions.
+    public static final int FINGERPRINT_ERROR_HW_UNAVAILABLE = 1;
+    public static final int FINGERPRINT_ERROR_BAD_CAPTURE = 2;
+    public static final int FINGERPRINT_ERROR_TIMEOUT = 3;
+    public static final int FINGERPRINT_ERROR_NO_SPACE = 4;
+
+    private IFingerprintService mService;
+    private FingerprintManagerReceiver mClientReceiver;
+
+    private Handler mHandler = new Handler() {
+        public void handleMessage(android.os.Message msg) {
+            if (mClientReceiver != null) {
+                switch(msg.what) {
+                    case MSG_ENROLL_RESULT:
+                        mClientReceiver.onEnrollResult(msg.arg1, msg.arg2);
+                        break;
+                    case MSG_SCANNED:
+                        mClientReceiver.onScanned(msg.arg1, msg.arg2);
+                        break;
+                    case MSG_ERROR:
+                        mClientReceiver.onError(msg.arg1);
+                        break;
+                    case MSG_REMOVED:
+                        mClientReceiver.onRemoved(msg.arg1);
+                }
+            }
+        }
+    };
+
+    public FingerprintManager(Context context) {
+        // Connect to service...
+        Intent intent = new Intent();
+        intent.setClassName(FINGERPRINT_SERVICE_PACKAGE, FINGERPRINT_SERVICE_CLASS);
+        if (!context.bindServiceAsUser(intent, mFingerprintConnection,
+                Context.BIND_AUTO_CREATE, UserHandle.CURRENT_OR_SELF)) {
+            if (DEBUG) Log.v(TAG, "Can't bind to " + FINGERPRINT_SERVICE_CLASS);
+        }
+    }
+
+    private final ServiceConnection mFingerprintConnection = new ServiceConnection() {
+        @Override
+        public void onServiceConnected(ComponentName name, IBinder service) {
+            if (DEBUG) Log.v(TAG, "Connected to FingerprintService");
+            mService = IFingerprintService.Stub.asInterface(service);
+            try {
+                mService.startListening(mServiceReceiver, getCurrentUserId());
+            } catch (RemoteException e) {
+                if (DEBUG) Log.v(TAG, "Failed to set callback", e);
+            }
+        }
+
+        @Override
+        public void onServiceDisconnected(ComponentName name) {
+            if (DEBUG) Log.v(TAG, "Disconnected from FingerprintService");
+            mService = null;
+        }
+    };
+
+    private IFingerprintServiceReceiver mServiceReceiver = new IFingerprintServiceReceiver.Stub() {
+
+        public void onEnrollResult(int fingerprintId,  int remaining) {
+            mHandler.obtainMessage(MSG_ENROLL_RESULT, fingerprintId, remaining).sendToTarget();
+        }
+
+        public void onScanned(int fingerprintId, int confidence) {
+            mHandler.obtainMessage(MSG_SCANNED, fingerprintId, confidence)
+                    .sendToTarget();;
+        }
+
+        public void onError(int error) {
+            mHandler.obtainMessage(MSG_ERROR, error, 0).sendToTarget();
+        }
+
+        public void onRemoved(int fingerprintId) {
+            mHandler.obtainMessage(MSG_REMOVED, fingerprintId, 0).sendToTarget();
+        }
+    };
+
+    /**
+     * Start the enrollment process.  Timeout dictates how long to wait for the user to
+     * enroll a fingerprint.
+     *
+     * @param timeout
+     */
+    public void enroll(long timeout) {
+        if (mServiceReceiver == null) {
+            throw new IllegalStateException("enroll: Call registerCallback() first");
+        }
+        if (mService != null) try {
+            mService.enroll(timeout, getCurrentUserId());
+        } catch (RemoteException e) {
+            Log.v(TAG, "Remote exception while enrolling: ", e);
+        }
+    }
+
+    /**
+     * Remove the given fingerprintId from the system.  FingerprintId of 0 has special meaning
+     * which is to delete all fingerprint data for the current user. Use with caution.
+     * @param fingerprintId
+     */
+    public void remove(int fingerprintId) {
+        if (mService != null) try {
+            mService.remove(fingerprintId, getCurrentUserId());
+        } catch (RemoteException e) {
+            Log.v(TAG, "Remote exception during remove of fingerprintId: " + fingerprintId, e);
+        }
+    }
+
+    /**
+     * Starts listening for fingerprint events.  When a finger is scanned or recognized, the
+     * client will be notified via the callback.
+     */
+    public void startListening(FingerprintManagerReceiver receiver) {
+        mClientReceiver = receiver;
+        if (mService != null) {
+            try {
+                mService.startListening(mServiceReceiver, getCurrentUserId());
+            } catch (RemoteException e) {
+                Log.v(TAG, "Remote exception in startListening(): ", e);
+            }
+        }
+    }
+
+    private int getCurrentUserId() {
+        try {
+            return ActivityManagerNative.getDefault().getCurrentUser().id;
+        } catch (RemoteException e) {
+            Log.w(TAG, "Failed to get current user id\n");
+            return UserHandle.USER_NULL;
+        }
+    }
+
+    /**
+     * Stops the client from listening to fingerprint events.
+     */
+    public void stopListening() {
+        mClientReceiver = null;
+        if (mService != null) {
+            try {
+                mService.stopListening(getCurrentUserId());
+            } catch (RemoteException e) {
+                Log.v(TAG, "Remote exception in stopListening(): ", e);
+            }
+        } else {
+            Log.w(TAG, "stopListening(): Service not connected!");
+        }
+    }
+}
\ No newline at end of file
diff --git a/core/java/android/service/fingerprint/FingerprintManagerReceiver.java b/core/java/android/service/fingerprint/FingerprintManagerReceiver.java
new file mode 100644
index 0000000..34f1655
--- /dev/null
+++ b/core/java/android/service/fingerprint/FingerprintManagerReceiver.java
@@ -0,0 +1,59 @@
+package android.service.fingerprint;
+/**
+ * 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.
+ */
+
+public class FingerprintManagerReceiver {
+    /**
+     * Fingerprint enrollment progress update. Enrollment is considered complete if
+     * remaining hits 0 without {@link #onError(int)} being called.
+     *
+     * @param fingerprintId the fingerprint we're currently enrolling
+     * @param remaining the number of samples required to complete enrollment. It's up to
+     * the hardware to define what each step in enrollment means. Some hardware
+     * requires multiple samples of the same part of the finger.  Others require sampling of
+     * different parts of the finger.  The enrollment flow can use remaining to
+     * mean "step x" of the process or "just need another sample."
+     */
+    public void onEnrollResult(int fingerprintId,  int remaining) { }
+
+    /**
+     * Fingerprint scan detected. Most clients will use this function to detect a fingerprint
+     *
+     * @param fingerprintId is the finger the hardware has detected.
+     * @param confidence from 0 (no confidence) to 65535 (high confidence). Fingerprint 0 has
+     * special meaning - the finger wasn't recognized.
+     */
+    public void onScanned(int fingerprintId, int confidence) { }
+
+    /**
+     * An error was detected during scan or enrollment.  One of
+     * {@link FingerprintManager#FINGERPRINT_ERROR_HW_UNAVAILABLE},
+     * {@link FingerprintManager#FINGERPRINT_ERROR_BAD_CAPTURE} or
+     * {@link FingerprintManager#FINGERPRINT_ERROR_TIMEOUT}
+     * {@link FingerprintManager#FINGERPRINT_ERROR_NO_SPACE}
+     *
+     * @param error one of the above error codes
+     */
+    public void onError(int error) { }
+
+    /**
+     * The given fingerprint template was successfully removed by the driver.
+     * See {@link FingerprintManager#remove(int)}
+     *
+     * @param fingerprintId id of template to remove.
+     */
+    public void onRemoved(int fingerprintId) { }
+}
\ No newline at end of file
diff --git a/core/java/android/service/fingerprint/FingerprintService.java b/core/java/android/service/fingerprint/FingerprintService.java
new file mode 100644
index 0000000..c7fa7cd
--- /dev/null
+++ b/core/java/android/service/fingerprint/FingerprintService.java
@@ -0,0 +1,219 @@
+/**
+ * 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.fingerprint;
+
+import android.app.Service;
+import android.content.ContentResolver;
+import android.content.Intent;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.provider.Settings;
+import android.util.Slog;
+
+import java.io.PrintWriter;
+import java.util.HashMap;
+
+/**
+ * A service to manage multiple clients that want to access the fingerprint HAL API.
+ * The service is responsible for maintaining a list of clients and dispatching all
+ * fingerprint -related events.
+ *
+ * @hide
+ */
+public class FingerprintService extends Service {
+    private final String TAG = FingerprintService.class.getSimpleName() +
+            "[" + getClass().getSimpleName() + "]";
+    private static final boolean DEBUG = true;
+    HashMap<IFingerprintServiceReceiver, ClientData> mClients =
+            new HashMap<IFingerprintServiceReceiver, ClientData>();
+
+    private static final int MSG_NOTIFY = 10;
+
+    Handler mHandler = new Handler() {
+        public void handleMessage(android.os.Message msg) {
+            switch (msg.what) {
+                case MSG_NOTIFY:
+                    handleNotify(msg.arg1, msg.arg2, (Integer) msg.obj);
+                    break;
+
+                default:
+                    Slog.w(TAG, "Unknown message:" + msg.what);
+            }
+        }
+    };
+
+    private static final int STATE_IDLE = 0;
+    private static final int STATE_LISTENING = 1;
+    private static final int STATE_ENROLLING = 2;
+    private static final int STATE_DELETING = 3;
+    private static final long MS_PER_SEC = 1000;
+
+    private static final class ClientData {
+        public IFingerprintServiceReceiver receiver;
+        int state;
+        int userId;
+    }
+
+    @Override
+    public final IBinder onBind(Intent intent) {
+        if (DEBUG) Slog.v(TAG, "onBind() intent = " + intent);
+        return new FingerprintServiceWrapper();
+    }
+
+    // JNI methods to communicate from FingerprintManagerService to HAL
+    native int nativeEnroll(int timeout);
+    native int nativeRemove(int fingerprintId);
+
+    // JNI methods for communicating from HAL to clients
+    void notify(int msg, int arg1, int arg2) {
+        mHandler.obtainMessage(MSG_NOTIFY, msg, arg1, arg2).sendToTarget();
+    }
+
+    void handleNotify(int msg, int arg1, int arg2) {
+        for (int i = 0; i < mClients.size(); i++) {
+            ClientData clientData = mClients.get(i);
+            switch (msg) {
+                case FingerprintManager.FINGERPRINT_ERROR: {
+                    if (clientData.state != STATE_IDLE) {
+                        // FINGERPRINT_ERROR_HW_UNAVAILABLE
+                        // FINGERPRINT_ERROR_BAD_CAPTURE
+                        // FINGERPRINT_ERROR_TIMEOUT
+                        // FINGERPRINT_ERROR_NO_SPACE
+                        final int error = arg1;
+                        clientData.state = STATE_IDLE;
+                        if (clientData.receiver != null) {
+                            try {
+                                clientData.receiver.onError(error);
+                            } catch (RemoteException e) {
+                                Slog.e(TAG, "can't send message to client. Did it die?", e);
+                            }
+                        }
+                    }
+                }
+                break;
+                case FingerprintManager.FINGERPRINT_SCANNED: {
+                    final int fingerId = arg1;
+                    final int confidence = arg2;
+                    if (clientData.state == STATE_LISTENING && clientData.receiver != null) {
+                        try {
+                            clientData.receiver.onScanned(fingerId, confidence);
+                        } catch (RemoteException e) {
+                            Slog.e(TAG, "can't send message to client. Did it die?", e);
+                        }
+                    }
+                    break;
+                }
+                case FingerprintManager.FINGERPRINT_TEMPLATE_ENROLLING: {
+                    if (clientData.state == STATE_ENROLLING) {
+                        final int fingerId = arg1;
+                        final int remaining = arg2;
+                        if (remaining == 0) {
+                            FingerprintUtils.addFingerprintIdForUser(fingerId,
+                                    getContentResolver(), clientData.userId);
+                            clientData.state = STATE_IDLE; // Nothing left to do
+                        }
+                        if (clientData.receiver != null) {
+                            try {
+                                clientData.receiver.onEnrollResult(fingerId, remaining);
+                            } catch (RemoteException e) {
+                                Slog.e(TAG, "can't send message to client. Did it die?", e);
+                            }
+                        }
+                    }
+                    break;
+                }
+                case FingerprintManager.FINGERPRINT_TEMPLATE_REMOVED: {
+                    int fingerId = arg1;
+                    if (fingerId == 0) throw new IllegalStateException("Got illegal id from HAL");
+                    if (clientData.state == STATE_DELETING) {
+                        FingerprintUtils.removeFingerprintIdForUser(fingerId, getContentResolver(),
+                                clientData.userId);
+                        if (clientData.receiver != null) {
+                            try {
+                                clientData.receiver.onRemoved(fingerId);
+                            } catch (RemoteException e) {
+                                Slog.e(TAG, "can't send message to client. Did it die?", e);
+                            }
+                        }
+                    }
+                }
+                break;
+            }
+        }
+    }
+
+    int enroll(IFingerprintServiceReceiver receiver, long timeout, int userId) {
+        ClientData clientData = mClients.get(receiver);
+        if (clientData != null) {
+            if (clientData.userId != userId) throw new IllegalStateException("Bad user");
+            clientData.state = STATE_ENROLLING;
+            return nativeEnroll((int) (timeout / MS_PER_SEC));
+        }
+        return -1;
+    }
+
+    int remove(IFingerprintServiceReceiver receiver, int fingerId, int userId) {
+        ClientData clientData = mClients.get(receiver);
+        if (clientData != null) {
+            if (clientData.userId != userId) throw new IllegalStateException("Bad user");
+            clientData.state = STATE_DELETING;
+            // The fingerprint id will be removed when we get confirmation from the HAL
+            return nativeRemove(fingerId);
+        }
+        return -1;
+    }
+
+    void startListening(IFingerprintServiceReceiver receiver, int userId) {
+        ClientData clientData = new ClientData();
+        clientData.state = STATE_LISTENING;
+        clientData.receiver = receiver;
+        clientData.userId = userId;
+        mClients.put(receiver, clientData);
+    }
+
+    void stopListening(IFingerprintServiceReceiver receiver, int userId) {
+        ClientData clientData = mClients.get(receiver);
+        if (clientData != null) {
+            clientData.state = STATE_IDLE;
+            clientData.userId = -1;
+            clientData.receiver = null;
+        }
+        mClients.remove(receiver);
+    }
+
+    private final class FingerprintServiceWrapper extends IFingerprintService.Stub {
+        IFingerprintServiceReceiver mReceiver;
+        public int enroll(long timeout, int userId) {
+            return mReceiver != null ? FingerprintService.this.enroll(mReceiver, timeout, userId)
+                    : FingerprintManager.FINGERPRINT_ERROR_NO_RECEIVER;
+        }
+
+        public int remove(int fingerprintId, int userId) {
+            return FingerprintService.this.remove(mReceiver, fingerprintId, userId);
+        }
+
+        public void startListening(IFingerprintServiceReceiver receiver, int userId) {
+            mReceiver = receiver;
+            FingerprintService.this.startListening(receiver, userId);
+        }
+
+        public void stopListening(int userId) {
+            FingerprintService.this.stopListening(mReceiver, userId);
+        }
+    }
+}
diff --git a/core/java/android/service/fingerprint/FingerprintUtils.java b/core/java/android/service/fingerprint/FingerprintUtils.java
new file mode 100644
index 0000000..81a2aac
--- /dev/null
+++ b/core/java/android/service/fingerprint/FingerprintUtils.java
@@ -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.
+ */
+
+package android.service.fingerprint;
+
+import android.content.ContentResolver;
+import android.provider.Settings;
+import android.util.Log;
+
+import java.util.Arrays;
+
+class FingerprintUtils {
+    private static final boolean DEBUG = true;
+    private static final String TAG = "FingerprintUtils";
+
+    public static int[] getFingerprintIdsForUser(ContentResolver res, int userId) {
+        String fingerIdsRaw = Settings.Secure.getStringForUser(res,
+                Settings.Secure.USER_FINGERPRINT_IDS, userId);
+
+        String[] fingerStringIds = fingerIdsRaw.replace("[","").replace("]","").split(", ");
+        int result[] = new int[fingerStringIds.length];
+        for (int i = 0; i < result.length; i++) {
+            try {
+                result[i] = Integer.decode(fingerStringIds[i]);
+            } catch (NumberFormatException e) {
+                if (DEBUG) Log.d(TAG, "Error when parsing finger id " + fingerStringIds[i]);
+            }
+        }
+        return result;
+    }
+
+    public static void addFingerprintIdForUser(int fingerId, ContentResolver res, int userId) {
+        int[] fingerIds = getFingerprintIdsForUser(res, userId);
+
+        // FingerId 0 has special meaning.
+        if (fingerId == 0) return;
+
+        // Don't allow dups
+        for (int i = 0; i < fingerIds.length; i++) {
+            if (fingerIds[i] == fingerId) return;
+        }
+        int[] newList = Arrays.copyOf(fingerIds, fingerIds.length + 1);
+        newList[fingerIds.length] = fingerId;
+        Settings.Secure.putStringForUser(res, Settings.Secure.USER_FINGERPRINT_IDS,
+                Arrays.toString(newList), userId);
+    }
+
+    public static boolean removeFingerprintIdForUser(int fingerId, ContentResolver res, int userId)
+    {
+        // FingerId 0 has special meaning. The HAL layer is supposed to remove each finger one
+        // at a time and invoke notify() for each fingerId.  If we get called with 0 here, it means
+        // something bad has happened.
+        if (fingerId == 0) throw new IllegalStateException("Bad fingerId");
+
+        int[] fingerIds = getFingerprintIdsForUser(res, userId);
+        int[] resultIds = Arrays.copyOf(fingerIds, fingerIds.length);
+        int resultCount = 0;
+        for (int i = 0; i < fingerIds.length; i++) {
+            if (fingerId != fingerIds[i]) {
+                resultIds[resultCount++] = fingerIds[i];
+            }
+        }
+        if (resultCount > 0) {
+            Settings.Secure.putStringForUser(res, Settings.Secure.USER_FINGERPRINT_IDS,
+                    Arrays.toString(Arrays.copyOf(resultIds, resultCount)), userId);
+            return true;
+        }
+        return false;
+    }
+
+};
+
diff --git a/core/java/android/service/fingerprint/IFingerprintService.aidl b/core/java/android/service/fingerprint/IFingerprintService.aidl
new file mode 100644
index 0000000..e92c20c
--- /dev/null
+++ b/core/java/android/service/fingerprint/IFingerprintService.aidl
@@ -0,0 +1,38 @@
+/*
+ * 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.fingerprint;
+
+import android.os.Bundle;
+import android.service.fingerprint.IFingerprintServiceReceiver;
+
+/**
+ * Communication channel from client to the fingerprint service.
+ * @hide
+ */
+interface IFingerprintService {
+    // Returns 0 if successfully started, -1 otherwise
+    int enroll(long timeout, int userId);
+
+    // Returns 0 if fingerprintId's template can be removed, -1 otherwise
+    int remove(int fingerprintId, int userId);
+
+    // Start listening for fingerprint events.  This has the side effect of starting
+    // the hardware if not already started.
+    oneway void startListening(IFingerprintServiceReceiver receiver, int userId);
+
+    // Stops listening for fingerprints
+    oneway void stopListening(int userId);
+}
diff --git a/core/java/android/service/fingerprint/IFingerprintServiceReceiver.aidl b/core/java/android/service/fingerprint/IFingerprintServiceReceiver.aidl
new file mode 100644
index 0000000..4826b59
--- /dev/null
+++ b/core/java/android/service/fingerprint/IFingerprintServiceReceiver.aidl
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.service.fingerprint;
+
+import android.os.Bundle;
+import android.os.UserHandle;
+
+/**
+ * Communication channel from the FingerprintService back to FingerprintManager.
+ * @hide
+ */
+oneway interface IFingerprintServiceReceiver {
+    void onEnrollResult(int fingerprintId,  int remaining);
+    void onScanned(int fingerprintId, int confidence);
+    void onError(int error);
+    void onRemoved(int fingerprintId);
+}
diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java
index 11948b2..f234baa 100644
--- a/core/java/android/view/GLES20Canvas.java
+++ b/core/java/android/view/GLES20Canvas.java
@@ -264,27 +264,6 @@
 
     private static native int nCallDrawGLFunction(long renderer, long drawGLFunction);
 
-    @Override
-    public int invokeFunctors(Rect dirty) {
-        return nInvokeFunctors(mRenderer, dirty);
-    }
-
-    private static native int nInvokeFunctors(long renderer, Rect dirty);
-
-    @Override
-    public void detachFunctor(long functor) {
-        nDetachFunctor(mRenderer, functor);
-    }
-
-    private static native void nDetachFunctor(long renderer, long functor);
-
-    @Override
-    public void attachFunctor(long functor) {
-        nAttachFunctor(mRenderer, functor);
-    }
-
-    private static native void nAttachFunctor(long renderer, long functor);
-
     ///////////////////////////////////////////////////////////////////////////
     // Memory
     ///////////////////////////////////////////////////////////////////////////
diff --git a/core/java/android/view/GLRenderer.java b/core/java/android/view/GLRenderer.java
index 97339cc..7b49006 100644
--- a/core/java/android/view/GLRenderer.java
+++ b/core/java/android/view/GLRenderer.java
@@ -168,7 +168,6 @@
     private final Rect mRedrawClip = new Rect();
 
     private final int[] mSurfaceSize = new int[2];
-    private final FunctorsRunnable mFunctorsRunnable = new FunctorsRunnable();
 
     private long mDrawDelta = Long.MAX_VALUE;
 
@@ -654,6 +653,11 @@
     }
 
     @Override
+    void setOpaque(boolean opaque) {
+        // Not supported
+    }
+
+    @Override
     boolean loadSystemProperties() {
         boolean value;
         boolean changed = false;
@@ -1116,22 +1120,6 @@
         mName = name;
     }
 
-    class FunctorsRunnable implements Runnable {
-        View.AttachInfo attachInfo;
-
-        @Override
-        public void run() {
-            final HardwareRenderer renderer = attachInfo.mHardwareRenderer;
-            if (renderer == null || !renderer.isEnabled() || renderer != GLRenderer.this) {
-                return;
-            }
-
-            if (checkRenderContext() != SURFACE_STATE_ERROR) {
-                mCanvas.invokeFunctors(mRedrawClip);
-            }
-        }
-    }
-
     @Override
     void draw(View view, View.AttachInfo attachInfo, HardwareDrawCallbacks callbacks,
             Rect dirty) {
@@ -1366,23 +1354,6 @@
         }
     }
 
-    @Override
-    void detachFunctor(long functor) {
-        if (mCanvas != null) {
-            mCanvas.detachFunctor(functor);
-        }
-    }
-
-    @Override
-    void attachFunctor(View.AttachInfo attachInfo, long functor) {
-        if (mCanvas != null) {
-            mCanvas.attachFunctor(functor);
-            mFunctorsRunnable.attachInfo = attachInfo;
-            attachInfo.mHandler.removeCallbacks(mFunctorsRunnable);
-            attachInfo.mHandler.postDelayed(mFunctorsRunnable,  0);
-        }
-    }
-
     /**
      * Ensures the current EGL context and surface are the ones we expect.
      * This method throws an IllegalStateException if invoked from a thread
diff --git a/core/java/android/view/HardwareCanvas.java b/core/java/android/view/HardwareCanvas.java
index 7ec2cc6..9568760 100644
--- a/core/java/android/view/HardwareCanvas.java
+++ b/core/java/android/view/HardwareCanvas.java
@@ -111,45 +111,6 @@
     }
 
     /**
-     * Invoke all the functors who requested to be invoked during the previous frame.
-     *
-     * @param dirty Ignored
-     *
-     * @return Ignored
-     *
-     * @hide
-     */
-    public int invokeFunctors(Rect dirty) {
-        return RenderNode.STATUS_DONE;
-    }
-
-    /**
-     * Detaches the specified functor from the current functor execution queue.
-     *
-     * @param functor The native functor to remove from the execution queue.
-     *
-     * @see #invokeFunctors(android.graphics.Rect)
-     * @see #callDrawGLFunction(long)
-     * @see #detachFunctor(long)
-     *
-     * @hide
-     */
-    abstract void detachFunctor(long functor);
-
-    /**
-     * Attaches the specified functor to the current functor execution queue.
-     *
-     * @param functor The native functor to add to the execution queue.
-     *
-     * @see #invokeFunctors(android.graphics.Rect)
-     * @see #callDrawGLFunction(long)
-     * @see #detachFunctor(long)
-     *
-     * @hide
-     */
-    abstract void attachFunctor(long functor);
-
-    /**
      * Indicates that the specified layer must be updated as soon as possible.
      *
      * @param layer The layer to update
diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java
index d31c79d..e366697 100644
--- a/core/java/android/view/HardwareRenderer.java
+++ b/core/java/android/view/HardwareRenderer.java
@@ -423,28 +423,6 @@
     abstract boolean copyLayerInto(HardwareLayer layer, Bitmap bitmap);
 
     /**
-     * Detaches the specified functor from the current functor execution queue.
-     *
-     * @param functor The native functor to remove from the execution queue.
-     *
-     * @see HardwareCanvas#callDrawGLFunction(int)
-     * @see #attachFunctor(android.view.View.AttachInfo, long)
-     */
-    abstract void detachFunctor(long functor);
-
-    /**
-     * Schedules the specified functor in the functors execution queue.
-     *
-     * @param attachInfo AttachInfo tied to this renderer.
-     * @param functor The native functor to insert in the execution queue.
-     *
-     * @see HardwareCanvas#callDrawGLFunction(int)
-     * @see #detachFunctor(long)
-     *
-     */
-    abstract void attachFunctor(View.AttachInfo attachInfo, long functor);
-
-    /**
      * Schedules the functor for execution in either kModeProcess or
      * kModeProcessNoContext, depending on whether or not there is an EGLContext.
      *
@@ -491,6 +469,11 @@
     abstract void setName(String name);
 
     /**
+     * Change the HardwareRenderer's opacity
+     */
+    abstract void setOpaque(boolean opaque);
+
+    /**
      * Creates a hardware renderer using OpenGL.
      *
      * @param translucent True if the surface is translucent, false otherwise
diff --git a/core/java/android/view/TextureView.java b/core/java/android/view/TextureView.java
index 3cfe5e9..1765c43 100644
--- a/core/java/android/view/TextureView.java
+++ b/core/java/android/view/TextureView.java
@@ -23,7 +23,6 @@
 import android.graphics.Paint;
 import android.graphics.Rect;
 import android.graphics.SurfaceTexture;
-import android.os.Looper;
 import android.util.AttributeSet;
 import android.util.Log;
 
@@ -119,8 +118,6 @@
     private boolean mUpdateLayer;
     private boolean mUpdateSurface;
 
-    private SurfaceTexture.OnFrameAvailableListener mUpdateListener;
-
     private Canvas mCanvas;
     private int mSaveCount;
 
@@ -370,21 +367,7 @@
             mSurface.setDefaultBufferSize(getWidth(), getHeight());
             nCreateNativeWindow(mSurface);
 
-            mUpdateListener = new SurfaceTexture.OnFrameAvailableListener() {
-                @Override
-                public void onFrameAvailable(SurfaceTexture surfaceTexture) {
-                    // Per SurfaceTexture's documentation, the callback may be invoked
-                    // from an arbitrary thread
-                    updateLayer();
-
-                    if (Looper.myLooper() == Looper.getMainLooper()) {
-                        invalidate();
-                    } else {
-                        postInvalidate();
-                    }
-                }
-            };
-            mSurface.setOnFrameAvailableListener(mUpdateListener);
+            mSurface.setOnFrameAvailableListener(mUpdateListener, mAttachInfo.mHandler);
 
             if (mListener != null && !mUpdateSurface) {
                 mListener.onSurfaceTextureAvailable(mSurface, getWidth(), getHeight());
@@ -422,7 +405,7 @@
             // To cancel updates, the easiest thing to do is simply to remove the
             // updates listener
             if (visibility == VISIBLE) {
-                mSurface.setOnFrameAvailableListener(mUpdateListener);
+                mSurface.setOnFrameAvailableListener(mUpdateListener, mAttachInfo.mHandler);
                 updateLayerAndInvalidate();
             } else {
                 mSurface.setOnFrameAvailableListener(null);
@@ -767,6 +750,15 @@
         mListener = listener;
     }
 
+    private final SurfaceTexture.OnFrameAvailableListener mUpdateListener =
+            new SurfaceTexture.OnFrameAvailableListener() {
+        @Override
+        public void onFrameAvailable(SurfaceTexture surfaceTexture) {
+            updateLayer();
+            invalidate();
+        }
+    };
+
     /**
      * This listener can be used to be notified when the surface texture
      * associated with this texture view is available.
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index 0bf99d3..2587ba1 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -148,6 +148,11 @@
     }
 
     @Override
+    void setOpaque(boolean opaque) {
+        nSetOpaque(mNativeProxy, opaque);
+    }
+
+    @Override
     int getWidth() {
         return mWidth;
     }
@@ -215,16 +220,6 @@
     }
 
     @Override
-    void detachFunctor(long functor) {
-        // no-op, we never attach functors to need to detach them
-    }
-
-    @Override
-    void attachFunctor(AttachInfo attachInfo, long functor) {
-        invokeFunctor(functor, true);
-    }
-
-    @Override
     void invokeFunctor(long functor, boolean waitForCompletion) {
         nInvokeFunctor(mNativeProxy, functor, waitForCompletion);
     }
@@ -312,6 +307,7 @@
     private static native void nUpdateSurface(long nativeProxy, Surface window);
     private static native void nPauseSurface(long nativeProxy, Surface window);
     private static native void nSetup(long nativeProxy, int width, int height);
+    private static native void nSetOpaque(long nativeProxy, boolean opaque);
     private static native void nSetDisplayListData(long nativeProxy, long displayList,
             long newData);
     private static native int nSyncAndDrawFrame(long nativeProxy, long frameTimeNanos,
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 3998c04..d8fcfc5 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -6147,12 +6147,12 @@
             // call into it as a fallback in case we're in a class that overrides it
             // and has logic to perform.
             if (fitSystemWindows(insets.getSystemWindowInsets())) {
-                return insets.cloneWithSystemWindowInsetsConsumed();
+                return insets.consumeSystemWindowInsets();
             }
         } else {
             // We were called from within a direct call to fitSystemWindows.
             if (fitSystemWindowsInt(insets.getSystemWindowInsets())) {
-                return insets.cloneWithSystemWindowInsetsConsumed();
+                return insets.consumeSystemWindowInsets();
             }
         }
         return insets;
@@ -9766,6 +9766,7 @@
             invalidateViewProperty(false, true);
 
             invalidateParentIfNeededAndWasQuickRejected();
+            notifySubtreeAccessibilityStateChangedIfNeeded();
         }
     }
 
@@ -9809,6 +9810,7 @@
             invalidateViewProperty(false, true);
 
             invalidateParentIfNeededAndWasQuickRejected();
+            notifySubtreeAccessibilityStateChangedIfNeeded();
         }
     }
 
@@ -9852,6 +9854,7 @@
             invalidateViewProperty(false, true);
 
             invalidateParentIfNeededAndWasQuickRejected();
+            notifySubtreeAccessibilityStateChangedIfNeeded();
         }
     }
 
@@ -9887,6 +9890,7 @@
             invalidateViewProperty(false, true);
 
             invalidateParentIfNeededAndWasQuickRejected();
+            notifySubtreeAccessibilityStateChangedIfNeeded();
         }
     }
 
@@ -9922,6 +9926,7 @@
             invalidateViewProperty(false, true);
 
             invalidateParentIfNeededAndWasQuickRejected();
+            notifySubtreeAccessibilityStateChangedIfNeeded();
         }
     }
 
@@ -10083,6 +10088,8 @@
                 mPrivateFlags &= ~PFLAG_ALPHA_SET;
                 invalidateViewProperty(true, false);
                 mRenderNode.setAlpha(getFinalAlpha());
+                notifyViewAccessibilityStateChangedIfNeeded(
+                        AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
             }
         }
     }
@@ -10513,6 +10520,7 @@
             invalidateViewProperty(false, true);
 
             invalidateParentIfNeededAndWasQuickRejected();
+            notifySubtreeAccessibilityStateChangedIfNeeded();
         }
     }
 
@@ -10661,6 +10669,7 @@
             } else {
                 mRenderNode.setOutline(null);
             }
+            notifySubtreeAccessibilityStateChangedIfNeeded();
         }
     }
 
@@ -10815,6 +10824,7 @@
                 }
                 invalidateParentIfNeeded();
             }
+            notifySubtreeAccessibilityStateChangedIfNeeded();
         }
     }
 
@@ -10861,6 +10871,7 @@
                 }
                 invalidateParentIfNeeded();
             }
+            notifySubtreeAccessibilityStateChangedIfNeeded();
         }
     }
 
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 43bc0b6..4309366 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -4638,6 +4638,7 @@
         if (invalidate) {
             invalidateViewProperty(false, false);
         }
+        notifySubtreeAccessibilityStateChangedIfNeeded();
     }
 
     /**
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index db87394..34e749a 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -665,18 +665,9 @@
         mHandler.sendMessageAtFrontOfQueue(mHandler.obtainMessage(MSG_FLUSH_LAYER_UPDATES));
     }
 
-    public void attachFunctor(long functor) {
-        //noinspection SimplifiableIfStatement
-        if (mAttachInfo.mHardwareRenderer != null && mAttachInfo.mHardwareRenderer.isEnabled()) {
-            mAttachInfo.mHardwareRenderer.attachFunctor(mAttachInfo, functor);
-        }
-    }
-
     public void detachFunctor(long functor) {
+        // TODO: Make the resize buffer some other way to not need this block
         mBlockResizeBuffer = true;
-        if (mAttachInfo.mHardwareRenderer != null) {
-            mAttachInfo.mHardwareRenderer.detachFunctor(functor);
-        }
     }
 
     public boolean invokeFunctor(long functor, boolean waitForCompletion) {
@@ -6174,8 +6165,10 @@
     }
 
     void changeCanvasOpacity(boolean opaque) {
-        // TODO(romainguy): recreate Canvas (software or hardware) to reflect the opacity change.
         Log.d(TAG, "changeCanvasOpacity: opaque=" + opaque);
+        if (mAttachInfo.mHardwareRenderer != null) {
+            mAttachInfo.mHardwareRenderer.setOpaque(opaque);
+        }
     }
 
     class TakenSurfaceHolder extends BaseSurfaceHolder {
diff --git a/core/java/android/view/WindowInsets.java b/core/java/android/view/WindowInsets.java
index 2160efe..294f472 100644
--- a/core/java/android/view/WindowInsets.java
+++ b/core/java/android/view/WindowInsets.java
@@ -29,7 +29,7 @@
  * @see View.OnApplyWindowInsetsListener
  * @see View#onApplyWindowInsets(WindowInsets)
  */
-public class WindowInsets {
+public final class WindowInsets {
     private Rect mSystemWindowInsets;
     private Rect mWindowDecorInsets;
     private Rect mTempRect;
@@ -151,6 +151,7 @@
      * This can include action bars, title bars, toolbars, etc.</p>
      *
      * @return The left window decor inset
+     * @hide pending API
      */
     public int getWindowDecorInsetLeft() {
         return mWindowDecorInsets.left;
@@ -164,6 +165,7 @@
      * This can include action bars, title bars, toolbars, etc.</p>
      *
      * @return The top window decor inset
+     * @hide pending API
      */
     public int getWindowDecorInsetTop() {
         return mWindowDecorInsets.top;
@@ -177,6 +179,7 @@
      * This can include action bars, title bars, toolbars, etc.</p>
      *
      * @return The right window decor inset
+     * @hide pending API
      */
     public int getWindowDecorInsetRight() {
         return mWindowDecorInsets.right;
@@ -190,6 +193,7 @@
      * This can include action bars, title bars, toolbars, etc.</p>
      *
      * @return The bottom window decor inset
+     * @hide pending API
      */
     public int getWindowDecorInsetBottom() {
         return mWindowDecorInsets.bottom;
@@ -217,6 +221,7 @@
      * This can include action bars, title bars, toolbars, etc.</p>
      *
      * @return true if any of the window decor inset values are nonzero
+     * @hide pending API
      */
     public boolean hasWindowDecorInsets() {
         return mWindowDecorInsets.left != 0 || mWindowDecorInsets.top != 0 ||
@@ -246,13 +251,28 @@
         return mIsRound;
     }
 
-    public WindowInsets cloneWithSystemWindowInsetsConsumed() {
+    /**
+     * Returns a copy of this WindowInsets with the system window insets fully consumed.
+     *
+     * @return A modified copy of this WindowInsets
+     */
+    public WindowInsets consumeSystemWindowInsets() {
         final WindowInsets result = new WindowInsets(this);
         result.mSystemWindowInsets = new Rect(0, 0, 0, 0);
         return result;
     }
 
-    public WindowInsets cloneWithSystemWindowInsetsConsumed(boolean left, boolean top,
+    /**
+     * Returns a copy of this WindowInsets with selected system window insets fully consumed.
+     *
+     * @param left true to consume the left system window inset
+     * @param top true to consume the top system window inset
+     * @param right true to consume the right system window inset
+     * @param bottom true to consume the bottom system window inset
+     * @return A modified copy of this WindowInsets
+     * @hide pending API
+     */
+    public WindowInsets consumeSystemWindowInsets(boolean left, boolean top,
             boolean right, boolean bottom) {
         if (left || top || right || bottom) {
             final WindowInsets result = new WindowInsets(this);
@@ -265,19 +285,36 @@
         return this;
     }
 
-    public WindowInsets cloneWithSystemWindowInsets(int left, int top, int right, int bottom) {
+    /**
+     * Returns a copy of this WindowInsets with selected system window insets replaced
+     * with new values.
+     *
+     * @param left New left inset in pixels
+     * @param top New top inset in pixels
+     * @param right New right inset in pixels
+     * @param bottom New bottom inset in pixels
+     * @return A modified copy of this WindowInsets
+     */
+    public WindowInsets replaceSystemWindowInsets(int left, int top,
+            int right, int bottom) {
         final WindowInsets result = new WindowInsets(this);
         result.mSystemWindowInsets = new Rect(left, top, right, bottom);
         return result;
     }
 
-    public WindowInsets cloneWithWindowDecorInsetsConsumed() {
+    /**
+     * @hide
+     */
+    public WindowInsets consumeWindowDecorInsets() {
         final WindowInsets result = new WindowInsets(this);
         result.mWindowDecorInsets.set(0, 0, 0, 0);
         return result;
     }
 
-    public WindowInsets cloneWithWindowDecorInsetsConsumed(boolean left, boolean top,
+    /**
+     * @hide
+     */
+    public WindowInsets consumeWindowDecorInsets(boolean left, boolean top,
             boolean right, boolean bottom) {
         if (left || top || right || bottom) {
             final WindowInsets result = new WindowInsets(this);
@@ -290,7 +327,10 @@
         return this;
     }
 
-    public WindowInsets cloneWithWindowDecorInsets(int left, int top, int right, int bottom) {
+    /**
+     * @hide
+     */
+    public WindowInsets replaceWindowDecorInsets(int left, int top, int right, int bottom) {
         final WindowInsets result = new WindowInsets(this);
         result.mWindowDecorInsets = new Rect(left, top, right, bottom);
         return result;
diff --git a/core/java/android/webkit/PermissionRequest.java b/core/java/android/webkit/PermissionRequest.java
index 2f8850b..3e33498 100644
--- a/core/java/android/webkit/PermissionRequest.java
+++ b/core/java/android/webkit/PermissionRequest.java
@@ -19,14 +19,11 @@
 import android.net.Uri;
 
 /**
- * This class wraps a permission request, and is used to request permission for
- * the web content to access the resources.
+ * This interface defines a permission request and is used when web content
+ * requests access to protected resources.
  *
- * Either {@link #grant(long) grant()} or {@link #deny()} must be called to response the
- * request, otherwise, {@link WebChromeClient#onPermissionRequest(PermissionRequest)} will
- * not be invoked again if there is other permission request in this WebView.
- *
- * @hide
+ * Either {@link #grant(long) grant()} or {@link #deny()} must be called in UI
+ * thread to respond to the request.
  */
 public interface PermissionRequest {
     /**
@@ -62,8 +59,6 @@
      *        must be equals or a subset of granted resources.
      *        This parameter is designed to avoid granting permission by accident
      *        especially when new resources are requested by web content.
-     *        Calling grant(getResources()) has security issue, the new permission
-     *        will be granted without being noticed.
      */
     public void grant(long resources);
 
diff --git a/core/java/android/webkit/WebChromeClient.java b/core/java/android/webkit/WebChromeClient.java
index 60cba86..d630a9a 100644
--- a/core/java/android/webkit/WebChromeClient.java
+++ b/core/java/android/webkit/WebChromeClient.java
@@ -304,7 +304,6 @@
      * If this method isn't overridden, the permission is denied.
      *
      * @param request the PermissionRequest from current web content.
-     * @hide
      */
     public void onPermissionRequest(PermissionRequest request) {
         request.deny();
@@ -314,8 +313,7 @@
      * Notify the host application that the given permission request
      * has been canceled. Any related UI should therefore be hidden.
      *
-     * @param request the PermissionRequest need be canceled.
-     * @hide
+     * @param request the PermissionRequest that needs be canceled.
      */
     public void onPermissionRequestCanceled(PermissionRequest request) {}
 
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 4b2b52c..91ca7b4 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -1682,13 +1682,15 @@
 
     /**
      * Preauthorize the given origin to access resources.
-     * This authorization only valid for this WebView instance life cycle and
+     * The authorization only valid for this WebView instance's life cycle and
      * will not retained.
      *
+     * In the case that an origin has had resources preauthorized, calls to
+     * {@link WebChromeClient#onPermissionRequest(PermissionRequest)} will not be
+     * made for those resources from that origin.
+     *
      * @param origin the origin authorized to access resources
      * @param resources the resource authorized to be accessed by origin.
-     *
-     * @hide
      */
     public void preauthorizePermission(Uri origin, long resources) {
         checkThread();
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index d1d1a52..75feb5d 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -33,7 +33,8 @@
     void animateCollapsePanels();
     void setSystemUiVisibility(int vis, int mask);
     void topAppWindowChanged(boolean menuVisible);
-    void setImeWindowStatus(in IBinder token, int vis, int backDisposition);
+    void setImeWindowStatus(in IBinder token, int vis, int backDisposition,
+            boolean showImeSwitcher);
     void setHardKeyboardStatus(boolean available, boolean enabled);
     void toggleRecentApps();
     void preloadRecentApps();
diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
index caa6b98..cf334c3 100644
--- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
@@ -31,7 +31,8 @@
     void setIconVisibility(String slot, boolean visible);
     void removeIcon(String slot);
     void topAppWindowChanged(boolean menuVisible);
-    void setImeWindowStatus(in IBinder token, int vis, int backDisposition);
+    void setImeWindowStatus(in IBinder token, int vis, int backDisposition,
+            boolean showImeSwitcher);
     void expandSettingsPanel();
     void setCurrentUser(int newUserId);
 
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index 667bf6c..8bd2e4f 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -141,6 +141,7 @@
 	android_util_FileObserver.cpp \
 	android/opengl/poly_clip.cpp.arm \
 	android/opengl/util.cpp.arm \
+	android_server_FingerprintManager.cpp \
 	android_server_NetworkManagementSocketTagger.cpp \
 	android_server_Watchdog.cpp \
 	android_ddm_DdmHandleNativeHeap.cpp \
diff --git a/core/jni/android/graphics/SurfaceTexture.cpp b/core/jni/android/graphics/SurfaceTexture.cpp
index b78b131..621534e 100644
--- a/core/jni/android/graphics/SurfaceTexture.cpp
+++ b/core/jni/android/graphics/SurfaceTexture.cpp
@@ -221,7 +221,7 @@
     }
 
     fields.postEvent = env->GetStaticMethodID(clazz, "postEventFromNative",
-            "(Ljava/lang/Object;)V");
+            "(Ljava/lang/ref/WeakReference;)V");
     if (fields.postEvent == NULL) {
         ALOGE("can't find android/graphics/SurfaceTexture.postEventFromNative");
     }
@@ -338,7 +338,7 @@
 
 static JNINativeMethod gSurfaceTextureMethods[] = {
     {"nativeClassInit",            "()V",   (void*)SurfaceTexture_classInit },
-    {"nativeInit",                 "(IZLjava/lang/Object;)V", (void*)SurfaceTexture_init },
+    {"nativeInit",                 "(IZLjava/lang/ref/WeakReference;)V", (void*)SurfaceTexture_init },
     {"nativeFinalize",             "()V",   (void*)SurfaceTexture_finalize },
     {"nativeSetDefaultBufferSize", "(II)V", (void*)SurfaceTexture_setDefaultBufferSize },
     {"nativeUpdateTexImage",       "()V",   (void*)SurfaceTexture_updateTexImage },
diff --git a/core/jni/android_server_FingerprintManager.cpp b/core/jni/android_server_FingerprintManager.cpp
new file mode 100644
index 0000000..f8a1fd9
--- /dev/null
+++ b/core/jni/android_server_FingerprintManager.cpp
@@ -0,0 +1,73 @@
+/*
+ * 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 "Fingerprint-JNI"
+
+#include "JNIHelp.h"
+
+#include <android_runtime/AndroidRuntime.h>
+#include <android_runtime/Log.h>
+#include <utils/Log.h>
+
+namespace android {
+
+static struct {
+    jclass clazz;
+    jmethodID notify;
+} gFingerprintManagerClassInfo;
+
+static jint nativeEnroll(JNIEnv* env, jobject clazz, jint timeout) {
+    return -1; // TODO
+}
+
+static jint nativeRemove(JNIEnv* env, jobject clazz, jint fingerprintId) {
+    return -1; // TODO
+}
+
+// ----------------------------------------------------------------------------
+
+static const JNINativeMethod g_methods[] = {
+    { "nativeEnroll", "(I)I", (void*)nativeEnroll },
+    { "nativeRemove", "(I)I", (void*)nativeRemove },
+};
+
+#define FIND_CLASS(var, className) \
+        var = env->FindClass(className); \
+        LOG_FATAL_IF(! var, "Unable to find class " className); \
+        var = jclass(env->NewGlobalRef(var));
+
+#define GET_STATIC_METHOD_ID(var, clazz, methodName, fieldDescriptor) \
+        var = env->GetStaticMethodID(clazz, methodName, fieldDescriptor); \
+        LOG_FATAL_IF(! var, "Unable to find static method" methodName);
+
+#define GET_METHOD_ID(var, clazz, methodName, fieldDescriptor) \
+        var = env->GetMethodID(clazz, methodName, fieldDescriptor); \
+        LOG_FATAL_IF(! var, "Unable to find method" methodName);
+
+#define GET_FIELD_ID(var, clazz, fieldName, fieldDescriptor) \
+        var = env->GetFieldID(clazz, fieldName, fieldDescriptor); \
+        LOG_FATAL_IF(! var, "Unable to find field " fieldName);
+
+int register_android_server_FingerprintManager(JNIEnv* env) {
+    FIND_CLASS(gFingerprintManagerClassInfo.clazz,
+            "android/service/fingerprint/FingerprintManager");
+    GET_METHOD_ID(gFingerprintManagerClassInfo.notify, gFingerprintManagerClassInfo.clazz,
+            "notify", "(III)V");
+    return AndroidRuntime::registerNativeMethods(
+        env, "com/android/service/fingerprint/FingerprintManager", g_methods, NELEM(g_methods));
+}
+
+} // namespace android
diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp
index 3aa179d..7b2f829 100644
--- a/core/jni/android_view_GLES20Canvas.cpp
+++ b/core/jni/android_view_GLES20Canvas.cpp
@@ -206,32 +206,6 @@
     return renderer->callDrawGLFunction(functor, dirty);
 }
 
-static void android_view_GLES20Canvas_detachFunctor(JNIEnv* env,
-        jobject clazz, jlong rendererPtr, jlong functorPtr) {
-    OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr);
-    Functor* functor = reinterpret_cast<Functor*>(functorPtr);
-    renderer->detachFunctor(functor);
-}
-
-static void android_view_GLES20Canvas_attachFunctor(JNIEnv* env,
-        jobject clazz, jlong rendererPtr, jlong functorPtr) {
-    OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr);
-    Functor* functor = reinterpret_cast<Functor*>(functorPtr);
-    renderer->attachFunctor(functor);
-}
-
-static jint android_view_GLES20Canvas_invokeFunctors(JNIEnv* env,
-        jobject clazz, jlong rendererPtr, jobject dirty) {
-    OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr);
-    android::uirenderer::Rect bounds;
-    status_t status = renderer->invokeFunctors(bounds);
-    if (status != DrawGlInfo::kStatusDone && dirty != NULL) {
-        env->CallVoidMethod(dirty, gRectClassInfo.set,
-                int(bounds.left), int(bounds.top), int(bounds.right), int(bounds.bottom));
-    }
-    return status;
-}
-
 // ----------------------------------------------------------------------------
 // Misc
 // ----------------------------------------------------------------------------
@@ -1007,10 +981,6 @@
     { "nGetStencilSize",    "()I",             (void*) android_view_GLES20Canvas_getStencilSize },
 
     { "nCallDrawGLFunction", "(JJ)I",          (void*) android_view_GLES20Canvas_callDrawGLFunction },
-    { "nDetachFunctor",      "(JJ)V",          (void*) android_view_GLES20Canvas_detachFunctor },
-    { "nAttachFunctor",      "(JJ)V",          (void*) android_view_GLES20Canvas_attachFunctor },
-    { "nInvokeFunctors",     "(JLandroid/graphics/Rect;)I",
-            (void*) android_view_GLES20Canvas_invokeFunctors },
 
     { "nSave",              "(JI)I",           (void*) android_view_GLES20Canvas_save },
     { "nRestore",           "(J)V",            (void*) android_view_GLES20Canvas_restore },
diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp
index 6ff28e3..cdd036e 100644
--- a/core/jni/android_view_ThreadedRenderer.cpp
+++ b/core/jni/android_view_ThreadedRenderer.cpp
@@ -197,6 +197,12 @@
     proxy->setup(width, height);
 }
 
+static void android_view_ThreadedRenderer_setOpaque(JNIEnv* env, jobject clazz,
+        jlong proxyPtr, jboolean opaque) {
+    RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
+    proxy->setOpaque(opaque);
+}
+
 static int android_view_ThreadedRenderer_syncAndDrawFrame(JNIEnv* env, jobject clazz,
         jlong proxyPtr, jlong frameTimeNanos, jint dirtyLeft, jint dirtyTop,
         jint dirtyRight, jint dirtyBottom) {
@@ -279,6 +285,7 @@
     { "nUpdateSurface", "(JLandroid/view/Surface;)V", (void*) android_view_ThreadedRenderer_updateSurface },
     { "nPauseSurface", "(JLandroid/view/Surface;)V", (void*) android_view_ThreadedRenderer_pauseSurface },
     { "nSetup", "(JII)V", (void*) android_view_ThreadedRenderer_setup },
+    { "nSetOpaque", "(JZ)V", (void*) android_view_ThreadedRenderer_setOpaque },
     { "nSyncAndDrawFrame", "(JJIIII)I", (void*) android_view_ThreadedRenderer_syncAndDrawFrame },
     { "nDestroyCanvasAndSurface", "(J)V", (void*) android_view_ThreadedRenderer_destroyCanvasAndSurface },
     { "nInvokeFunctor", "(JJZ)V", (void*) android_view_ThreadedRenderer_invokeFunctor },
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index cce4dbd..b1f256e 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -885,6 +885,47 @@
          be passed a persistable Bundle in their Intent.extras. -->
     <attr name="persistable" format="boolean" />
 
+    <!-- Specify whether this activity should always be launched in doc-centric mode. For
+         values other than <code>none</code> the activity must be defined with
+         {@link android.R.attr#launchMode} <code>standard</code> or <code>singleTop</code>.
+         This attribute can be overridden by {@link
+         android.content.Intent#FLAG_ACTIVITY_NEW_DOCUMENT}.
+
+         <p>If this attribute is not specified, <code>none</code> will be used.
+         Note that this launch behavior can be changed in some ways at runtime
+         through the {@link android.content.Intent} flags
+         {@link android.content.Intent#FLAG_ACTIVITY_NEW_DOCUMENT}. -->
+    <attr name="documentLaunchMode">
+        <!-- The default mode, which will create a new task only when
+             {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK
+             Intent.FLAG_ACTIVITY_NEW_TASK} is set. -->
+        <enum name="none" value="0" />
+        <!-- All tasks will be searched for a matching Intent. If one is found
+             That task will cleared and restarted with the root activity receiving a call
+             to {@link android.app.Activity#onNewIntent Activity.onNewIntent}. If no
+             such task is found a new task will be created.
+             This is the equivalent of with {@link
+             android.content.Intent#FLAG_ACTIVITY_NEW_DOCUMENT Intent.FLAG_ACTIVITY_NEW_DOCUMENT}
+             without {@link android.content.Intent#FLAG_ACTIVITY_MULTIPLE_TASK
+             Intent.FLAG_ACTIVITY_MULTIPLE_TASK}. -->
+        <enum name="intoExisting" value="1" />
+        <!-- A new task rooted at this activity will be created.
+             This is the equivalent of with {@link
+             android.content.Intent#FLAG_ACTIVITY_NEW_DOCUMENT Intent.FLAG_ACTIVITY_NEW_DOCUMENT}
+             paired with {@link android.content.Intent#FLAG_ACTIVITY_MULTIPLE_TASK
+             Intent.FLAG_ACTIVITY_MULTIPLE_TASK}. -->
+        <enum name="always" value="2" />
+    </attr>
+
+    <!-- Tasks launched by activities with this attribute will remain in the recent task
+         list until the last activity in the task is completed. When that happens the task
+         will be automatically removed from the recent task list.
+
+         This attribute is the equivalent of {@link
+         android.content.Intent#FLAG_ACTIVITY_AUTO_REMOVE_FROM_RECENTS
+         Intent.FLAG_ACTIVITY_AUTO_REMOVE_FROM_RECENTS} -->
+    <attr name="autoRemoveFromRecents" format="boolean" />
+
     <!-- The <code>manifest</code> tag is the root of an
          <code>AndroidManifest.xml</code> file,
          describing the contents of an Android package (.apk) file.  One
@@ -1549,6 +1590,8 @@
         <attr name="primaryUserOnly" format="boolean" />
         <attr name="persistable" />
         <attr name="allowEmbedded" />
+        <attr name="documentLaunchMode" />
+        <attr name="autoRemoveFromRecents" />
     </declare-styleable>
     
     <!-- The <code>activity-alias</code> tag declares a new
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index e88a6ee..8874c30 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2169,6 +2169,8 @@
   <public type="attr" name="excludeClass" />
   <public type="attr" name="hideOnContentScroll" />
   <public type="attr" name="actionOverflowMenuStyle" />
+  <public type="attr" name="documentLaunchMode" />
+  <public type="attr" name="autoRemoveFromRecents" />
 
   <public-padding type="dimen" name="l_resource_pad" end="0x01050010" />
 
diff --git a/core/res/res/values/themes_quantum.xml b/core/res/res/values/themes_quantum.xml
index 39c8beb..768fd9a 100644
--- a/core/res/res/values/themes_quantum.xml
+++ b/core/res/res/values/themes_quantum.xml
@@ -123,7 +123,7 @@
         <item name="listSeparatorTextViewStyle">@style/Widget.Quantum.TextView.ListSeparator</item>
 
         <item name="listChoiceIndicatorSingle">@drawable/btn_radio_quantum</item>
-        <item name="listChoiceIndicatorMultiple">@drawable/btn_check_quantum_anim</item>
+        <item name="listChoiceIndicatorMultiple">@drawable/btn_check_quantum</item>
 
         <item name="listChoiceBackgroundIndicator">@drawable/list_selector_quantum</item>
 
@@ -467,7 +467,7 @@
         <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_anim</item>
+        <item name="listChoiceIndicatorMultiple">@drawable/btn_check_quantum</item>
 
         <item name="listChoiceBackgroundIndicator">@drawable/list_selector_quantum</item>
 
diff --git a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothUuidTest.java b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothUuidTest.java
new file mode 100644
index 0000000..0f3ea0e
--- /dev/null
+++ b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothUuidTest.java
@@ -0,0 +1,50 @@
+/*
+ * 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.bluetooth;
+
+import android.os.ParcelUuid;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import junit.framework.TestCase;
+
+/**
+ * Unit test cases for {@link BluetoothUuid}.
+ * <p>
+ * To run this test, use adb shell am instrument -e class 'android.bluetooth.BluetoothUuidTest' -w
+ * 'com.android.bluetooth.tests/android.bluetooth.BluetoothTestRunner'
+ */
+public class BluetoothUuidTest extends TestCase {
+
+    @SmallTest
+    public void testUuidParser() {
+        byte[] uuid16 = new byte[] {
+                0x0B, 0x11 };
+        assertEquals(ParcelUuid.fromString("0000110B-0000-1000-8000-00805F9B34FB"),
+                BluetoothUuid.parseUuidFrom(uuid16));
+
+        byte[] uuid32 = new byte[] {
+                0x0B, 0x11, 0x33, (byte) 0xFE };
+        assertEquals(ParcelUuid.fromString("FE33110B-0000-1000-8000-00805F9B34FB"),
+                BluetoothUuid.parseUuidFrom(uuid32));
+
+        byte[] uuid128 = new byte[] {
+                0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+                0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, (byte) 0xFF };
+        assertEquals(ParcelUuid.fromString("FF0F0E0D-0C0B-0A09-0807-0060504030201"),
+                BluetoothUuid.parseUuidFrom(uuid128));
+    }
+}
diff --git a/graphics/java/android/graphics/SurfaceTexture.java b/graphics/java/android/graphics/SurfaceTexture.java
index d877502..3f8c45c 100644
--- a/graphics/java/android/graphics/SurfaceTexture.java
+++ b/graphics/java/android/graphics/SurfaceTexture.java
@@ -62,9 +62,8 @@
  * #updateTexImage} should not be called directly from the callback.
  */
 public class SurfaceTexture {
-
-    private EventHandler mEventHandler;
-    private OnFrameAvailableListener mOnFrameAvailableListener;
+    private final Looper mCreatorLooper;
+    private Handler mOnFrameAvailableHandler;
 
     /**
      * These fields are used by native code, do not access or modify.
@@ -83,7 +82,8 @@
     /**
      * Exception thrown when a SurfaceTexture couldn't be created or resized.
      *
-     * @deprecated No longer thrown. {@link Surface.OutOfResourcesException} is used instead.
+     * @deprecated No longer thrown. {@link android.view.Surface.OutOfResourcesException}
+     * is used instead.
      */
     @SuppressWarnings("serial")
     @Deprecated
@@ -100,10 +100,10 @@
      *
      * @param texName the OpenGL texture object name (e.g. generated via glGenTextures)
      *
-     * @throws OutOfResourcesException If the SurfaceTexture cannot be created.
+     * @throws Surface.OutOfResourcesException If the SurfaceTexture cannot be created.
      */
     public SurfaceTexture(int texName) {
-        init(texName, false);
+        this(texName, false);
     }
 
     /**
@@ -121,20 +121,58 @@
      * @param texName the OpenGL texture object name (e.g. generated via glGenTextures)
      * @param singleBufferMode whether the SurfaceTexture will be in single buffered mode.
      *
-     * @throws throws OutOfResourcesException If the SurfaceTexture cannot be created.
+     * @throws Surface.OutOfResourcesException If the SurfaceTexture cannot be created.
      */
     public SurfaceTexture(int texName, boolean singleBufferMode) {
-        init(texName, singleBufferMode);
+        mCreatorLooper = Looper.myLooper();
+        nativeInit(texName, singleBufferMode, new WeakReference<SurfaceTexture>(this));
     }
 
     /**
      * Register a callback to be invoked when a new image frame becomes available to the
-     * SurfaceTexture.  Note that this callback may be called on an arbitrary thread, so it is not
+     * SurfaceTexture.
+     * <p>
+     * This callback may be called on an arbitrary thread, so it is not
      * safe to call {@link #updateTexImage} without first binding the OpenGL ES context to the
      * thread invoking the callback.
+     * </p>
+     *
+     * @param listener The listener to set.
      */
-    public void setOnFrameAvailableListener(OnFrameAvailableListener l) {
-        mOnFrameAvailableListener = l;
+    public void setOnFrameAvailableListener(OnFrameAvailableListener listener) {
+        setOnFrameAvailableListener(listener, null);
+    }
+
+    /**
+     * Register a callback to be invoked when a new image frame becomes available to the
+     * SurfaceTexture.
+     * <p>
+     * If no handler is specified, then this callback may be called on an arbitrary thread,
+     * so it is not safe to call {@link #updateTexImage} without first binding the OpenGL ES
+     * context to the thread invoking the callback.
+     * </p>
+     *
+     * @param listener The listener to set.
+     * @param handler The handler on which the listener should be invoked, or null
+     * to use an arbitrary thread.
+     */
+    public void setOnFrameAvailableListener(final OnFrameAvailableListener listener,
+            Handler handler) {
+        if (listener != null) {
+            // Although we claim the thread is arbitrary, earlier implementation would
+            // prefer to send the callback on the creating looper or the main looper
+            // so we preserve this behavior here.
+            Looper looper = handler != null ? handler.getLooper() :
+                    mCreatorLooper != null ? mCreatorLooper : Looper.getMainLooper();
+            mOnFrameAvailableHandler = new Handler(looper, null, true /*async*/) {
+                @Override
+                public void handleMessage(Message msg) {
+                    listener.onFrameAvailable(SurfaceTexture.this);
+                }
+            };
+        } else {
+            mOnFrameAvailableHandler = null;
+        }
     }
 
     /**
@@ -285,49 +323,22 @@
         }
     }
 
-    private class EventHandler extends Handler {
-        public EventHandler(Looper looper) {
-            super(looper);
-        }
-
-        @Override
-        public void handleMessage(Message msg) {
-            if (mOnFrameAvailableListener != null) {
-                mOnFrameAvailableListener.onFrameAvailable(SurfaceTexture.this);
-            }
-        }
-    }
-
     /**
      * This method is invoked from native code only.
      */
     @SuppressWarnings({"UnusedDeclaration"})
-    private static void postEventFromNative(Object selfRef) {
-        WeakReference weakSelf = (WeakReference)selfRef;
-        SurfaceTexture st = (SurfaceTexture)weakSelf.get();
-        if (st == null) {
-            return;
-        }
-
-        if (st.mEventHandler != null) {
-            Message m = st.mEventHandler.obtainMessage();
-            st.mEventHandler.sendMessage(m);
+    private static void postEventFromNative(WeakReference<SurfaceTexture> weakSelf) {
+        SurfaceTexture st = weakSelf.get();
+        if (st != null) {
+            Handler handler = st.mOnFrameAvailableHandler;
+            if (handler != null) {
+                handler.sendEmptyMessage(0);
+            }
         }
     }
 
-    private void init(int texName, boolean singleBufferMode) throws Surface.OutOfResourcesException {
-        Looper looper;
-        if ((looper = Looper.myLooper()) != null) {
-            mEventHandler = new EventHandler(looper);
-        } else if ((looper = Looper.getMainLooper()) != null) {
-            mEventHandler = new EventHandler(looper);
-        } else {
-            mEventHandler = null;
-        }
-        nativeInit(texName, singleBufferMode, new WeakReference<SurfaceTexture>(this));
-    }
-
-    private native void nativeInit(int texName, boolean singleBufferMode, Object weakSelf)
+    private native void nativeInit(int texName, boolean singleBufferMode,
+            WeakReference<SurfaceTexture> weakSelf)
             throws Surface.OutOfResourcesException;
     private native void nativeFinalize();
     private native void nativeGetTransformMatrix(float[] mtx);
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 95fdb04..6de369c 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -379,49 +379,9 @@
     dirtyClip();
 }
 
-void OpenGLRenderer::detachFunctor(Functor* functor) {
-    mFunctors.remove(functor);
-}
-
-void OpenGLRenderer::attachFunctor(Functor* functor) {
-    mFunctors.add(functor);
-}
-
-status_t OpenGLRenderer::invokeFunctors(Rect& dirty) {
-    status_t result = DrawGlInfo::kStatusDone;
-    size_t count = mFunctors.size();
-
-    if (count > 0) {
-        interrupt();
-        SortedVector<Functor*> functors(mFunctors);
-        mFunctors.clear();
-
-        DrawGlInfo info;
-        info.clipLeft = 0;
-        info.clipTop = 0;
-        info.clipRight = 0;
-        info.clipBottom = 0;
-        info.isLayer = false;
-        info.width = 0;
-        info.height = 0;
-        memset(info.transform, 0, sizeof(float) * 16);
-
-        for (size_t i = 0; i < count; i++) {
-            Functor* f = functors.itemAt(i);
-            result |= (*f)(DrawGlInfo::kModeProcess, &info);
-        }
-        resume();
-    }
-
-    return result;
-}
-
 status_t OpenGLRenderer::callDrawGLFunction(Functor* functor, Rect& dirty) {
     if (currentSnapshot()->isIgnored()) return DrawGlInfo::kStatusDone;
 
-    detachFunctor(functor);
-
-
     Rect clip(*currentClipRect());
     clip.snapToPixelBoundaries();
 
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index 7794abc..4de52ac 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -151,9 +151,6 @@
         return mCountOverdraw ? mOverdraw : 0.0f;
     }
 
-    ANDROID_API status_t invokeFunctors(Rect& dirty);
-    ANDROID_API void detachFunctor(Functor* functor);
-    ANDROID_API void attachFunctor(Functor* functor);
     virtual status_t callDrawGLFunction(Functor* functor, Rect& dirty);
 
     ANDROID_API void pushLayerUpdate(Layer* layer);
@@ -959,8 +956,6 @@
 
     // List of rectangles to clear after saveLayer() is invoked
     Vector<Rect*> mLayers;
-    // List of functors to invoke after a frame is drawn
-    SortedVector<Functor*> mFunctors;
     // List of layers to update at the beginning of a frame
     Vector<Layer*> mLayerUpdates;
 
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index fc3548c..5a23158 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -349,6 +349,8 @@
         mDirtyRegionsEnabled = mGlobalContext->enableDirtyRegions(mEglSurface);
         mHaveNewSurface = true;
         makeCurrent();
+    } else {
+        mRenderThread.removeFrameCallback(this);
     }
 }
 
@@ -385,6 +387,10 @@
     mCanvas->setViewport(width, height);
 }
 
+void CanvasContext::setOpaque(bool opaque) {
+    mOpaque = opaque;
+}
+
 void CanvasContext::makeCurrent() {
     // TODO: Figure out why this workaround is needed, see b/13913604
     // In the meantime this matches the behavior of GLRenderer, so it is not a regression
@@ -468,6 +474,10 @@
 
 // Called by choreographer to do an RT-driven animation
 void CanvasContext::doFrame() {
+    if (CC_UNLIKELY(!mCanvas || mEglSurface == EGL_NO_SURFACE)) {
+        return;
+    }
+
     ATRACE_CALL();
 
     TreeInfo info;
@@ -486,10 +496,7 @@
         requireGlContext();
         mode = DrawGlInfo::kModeProcess;
     }
-    // TODO: Remove the dummy info in the future
-    DrawGlInfo dummyInfo;
-    memset(&dummyInfo, 0, sizeof(DrawGlInfo));
-    (*functor)(mode, &dummyInfo);
+    (*functor)(mode, NULL);
 
     if (mCanvas) {
         mCanvas->resume();
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index a95e27a..dcb9858 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -52,6 +52,7 @@
     void updateSurface(EGLNativeWindowType window);
     void pauseSurface(EGLNativeWindowType window);
     void setup(int width, int height);
+    void setOpaque(bool opaque);
     void makeCurrent();
     void prepareDraw(const Vector<DeferredLayerUpdater*>* layerUpdaters, TreeInfo& info);
     void draw(Rect* dirty);
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index c2806fa..82a2dbc 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -159,6 +159,18 @@
     post(task);
 }
 
+CREATE_BRIDGE2(setOpaque, CanvasContext* context, bool opaque) {
+    args->context->setOpaque(args->opaque);
+    return NULL;
+}
+
+void RenderProxy::setOpaque(bool opaque) {
+    SETUP_TASK(setOpaque);
+    args->context = mContext;
+    args->opaque = opaque;
+    post(task);
+}
+
 int RenderProxy::syncAndDrawFrame(nsecs_t frameTimeNanos,
         int dirtyLeft, int dirtyTop, int dirtyRight, int dirtyBottom) {
     mDrawFrameTask.setDirty(dirtyLeft, dirtyTop, dirtyRight, dirtyBottom);
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index 013c3bd..4a7e70a 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -67,6 +67,7 @@
     ANDROID_API void updateSurface(const sp<ANativeWindow>& window);
     ANDROID_API void pauseSurface(const sp<ANativeWindow>& window);
     ANDROID_API void setup(int width, int height);
+    ANDROID_API void setOpaque(bool opaque);
     ANDROID_API int syncAndDrawFrame(nsecs_t frameTimeNanos,
             int dirtyLeft, int dirtyTop, int dirtyRight, int dirtyBottom);
     ANDROID_API void destroyCanvasAndSurface();
diff --git a/media/java/android/media/session/ISession.aidl b/media/java/android/media/session/ISession.aidl
index ca77f04..3ff07d9 100644
--- a/media/java/android/media/session/ISession.aidl
+++ b/media/java/android/media/session/ISession.aidl
@@ -31,8 +31,8 @@
 interface ISession {
     void sendEvent(String event, in Bundle data);
     ISessionController getController();
-    void setTransportPerformerEnabled();
-    void publish();
+    void setFlags(int flags);
+    void setActive(boolean active);
     void destroy();
 
     // These commands are for setting up and communicating with routes
diff --git a/media/java/android/media/session/ISessionManager.aidl b/media/java/android/media/session/ISessionManager.aidl
index 84b9a0f..7a8c22e 100644
--- a/media/java/android/media/session/ISessionManager.aidl
+++ b/media/java/android/media/session/ISessionManager.aidl
@@ -15,6 +15,7 @@
 
 package android.media.session;
 
+import android.content.ComponentName;
 import android.media.session.ISession;
 import android.media.session.ISessionCallback;
 import android.os.Bundle;
@@ -25,4 +26,5 @@
  */
 interface ISessionManager {
     ISession createSession(String packageName, in ISessionCallback cb, String tag);
+    List<IBinder> getSessions(in ComponentName compName);
 }
\ No newline at end of file
diff --git a/media/java/android/media/session/MediaSessionLegacyHelper.java b/media/java/android/media/session/MediaSessionLegacyHelper.java
index 4ee67d1..c07229d 100644
--- a/media/java/android/media/session/MediaSessionLegacyHelper.java
+++ b/media/java/android/media/session/MediaSessionLegacyHelper.java
@@ -43,7 +43,8 @@
     private Handler mHandler = new Handler(Looper.getMainLooper());
     // The legacy APIs use PendingIntents to register/unregister media button
     // receivers and these are associated with RCC.
-    private ArrayMap<PendingIntent, SessionHolder> mSessions = new ArrayMap<PendingIntent, SessionHolder>();
+    private ArrayMap<PendingIntent, SessionHolder> mSessions
+            = new ArrayMap<PendingIntent, SessionHolder>();
 
     private MediaSessionLegacyHelper(Context context) {
         mSessionManager = (SessionManager) context
@@ -78,6 +79,8 @@
         }
         performer.addListener(listener, mHandler);
         holder.mRccListener = listener;
+        holder.mFlags |= Session.FLAG_HANDLES_TRANSPORT_CONTROLS;
+        holder.mSession.setFlags(holder.mFlags);
         holder.update();
     }
 
@@ -86,6 +89,8 @@
         if (holder != null && holder.mRccListener != null) {
             holder.mSession.getTransportPerformer().removeListener(holder.mRccListener);
             holder.mRccListener = null;
+            holder.mFlags &= ~Session.FLAG_HANDLES_TRANSPORT_CONTROLS;
+            holder.mSession.setFlags(holder.mFlags);
             holder.update();
         }
     }
@@ -98,6 +103,8 @@
             return;
         }
         holder.mMediaButtonListener = new MediaButtonListener(pi, context);
+        holder.mFlags |= Session.FLAG_HANDLES_MEDIA_BUTTONS;
+        holder.mSession.setFlags(holder.mFlags);
         holder.mSession.getTransportPerformer().addListener(holder.mMediaButtonListener, mHandler);
     }
 
@@ -105,6 +112,9 @@
         SessionHolder holder = getHolder(pi, false);
         if (holder != null && holder.mMediaButtonListener != null) {
             holder.mSession.getTransportPerformer().removeListener(holder.mMediaButtonListener);
+            holder.mFlags &= ~Session.FLAG_HANDLES_MEDIA_BUTTONS;
+            holder.mSession.setFlags(holder.mFlags);
+            holder.mMediaButtonListener = null;
             holder.update();
         }
     }
@@ -113,8 +123,7 @@
         SessionHolder holder = mSessions.get(pi);
         if (holder == null && createIfMissing) {
             Session session = mSessionManager.createSession(TAG);
-            session.setTransportPerformerEnabled();
-            session.publish();
+            session.setActive(true);
             holder = new SessionHolder(session, pi);
             mSessions.put(pi, holder);
         }
@@ -193,6 +202,7 @@
         public final PendingIntent mPi;
         public MediaButtonListener mMediaButtonListener;
         public TransportPerformer.Listener mRccListener;
+        public int mFlags;
 
         public SessionHolder(Session session, PendingIntent pi) {
             mSession = session;
diff --git a/media/java/android/media/session/Session.java b/media/java/android/media/session/Session.java
index 8ccd788..194679e7 100644
--- a/media/java/android/media/session/Session.java
+++ b/media/java/android/media/session/Session.java
@@ -45,12 +45,13 @@
  * media to multiple routes or to provide finer grain controls of media.
  * <p>
  * A MediaSession is created by calling
- * {@link SessionManager#createSession(String)}. Once a session is created
- * apps that have the MEDIA_CONTENT_CONTROL permission can interact with the
- * session through {@link SessionManager#getActiveSessions()}. The owner of
- * the session may also use {@link #getSessionToken()} to allow apps without
- * this permission to create a {@link SessionController} to interact with this
- * session.
+ * {@link SessionManager#createSession(String)}. Once a session is created apps
+ * that have the MEDIA_CONTENT_CONTROL permission can interact with the session
+ * through
+ * {@link SessionManager#getActiveSessions(android.content.ComponentName)}. The
+ * owner of the session may also use {@link #getSessionToken()} to allow apps
+ * without this permission to create a {@link SessionController} to interact
+ * with this session.
  * <p>
  * To receive commands, media keys, and other events a Callback must be set with
  * {@link #addCallback(Callback)}.
@@ -63,6 +64,28 @@
 public final class Session {
     private static final String TAG = "Session";
 
+    /**
+     * Set this flag on the session to indicate that it can handle media button
+     * events.
+     */
+    public static final int FLAG_HANDLES_MEDIA_BUTTONS = 1 << 0;
+
+    /**
+     * Set this flag on the session to indicate that it handles commands through
+     * the {@link TransportPerformer}. The performer can be retrieved by calling
+     * {@link #getTransportPerformer()}.
+     */
+    public static final int FLAG_HANDLES_TRANSPORT_CONTROLS = 1 << 1;
+
+    /**
+     * System only flag for a session that needs to have priority over all other
+     * sessions. This flag ensures this session will receive media button events
+     * regardless of the current ordering in the system.
+     *
+     * @hide
+     */
+    public static final int FLAG_EXCLUSIVE_GLOBAL_PRIORITY = 1 << 16;
+
     private static final int MSG_MEDIA_BUTTON = 1;
     private static final int MSG_COMMAND = 2;
     private static final int MSG_ROUTE_CHANGE = 3;
@@ -86,7 +109,7 @@
     private TransportPerformer mPerformer;
     private Route mRoute;
 
-    private boolean mPublished = false;;
+    private boolean mActive = false;;
 
     /**
      * @hide
@@ -101,6 +124,7 @@
             throw new RuntimeException("Dead object in MediaSessionController constructor: ", e);
         }
         mSessionToken = new SessionToken(controllerBinder);
+        mPerformer = new TransportPerformer(mBinder);
     }
 
     /**
@@ -148,56 +172,57 @@
     }
 
     /**
-     * Start using a TransportPerformer with this media session. This must be
-     * called before calling publish and cannot be called more than once.
-     * Calling this will allow MediaControllers to retrieve a
-     * TransportController.
+     * Retrieves the {@link TransportPerformer} for this session. To receive
+     * commands through the performer you must also set the
+     * {@link #FLAG_HANDLES_TRANSPORT_CONTROLS} flag using
+     * {@link #setFlags(int)}.
      *
-     * @see TransportController
-     * @return The TransportPerformer created for this session
-     */
-    public TransportPerformer setTransportPerformerEnabled() {
-        if (mPerformer != null) {
-            throw new IllegalStateException("setTransportPerformer can only be called once.");
-        }
-        if (mPublished) {
-            throw new IllegalStateException("setTransportPerformer cannot be called after publish");
-        }
-
-        mPerformer = new TransportPerformer(mBinder);
-        try {
-            mBinder.setTransportPerformerEnabled();
-        } catch (RemoteException e) {
-            Log.wtf(TAG, "Failure in setTransportPerformerEnabled.", e);
-        }
-        return mPerformer;
-    }
-
-    /**
-     * Retrieves the TransportPerformer used by this session. If called before
-     * {@link #setTransportPerformerEnabled} null will be returned.
-     *
-     * @return The TransportPerformer associated with this session or null
+     * @return The performer associated with this session.
      */
     public TransportPerformer getTransportPerformer() {
         return mPerformer;
     }
 
     /**
-     * Call after you have finished setting up the session. This will make it
-     * available to listeners and begin pushing updates to MediaControllers.
-     * This can only be called once.
+     * Set any flags for the session.
+     *
+     * @param flags The flags to set for this session.
      */
-    public void publish() {
-        if (mPublished) {
-            throw new RuntimeException("publish() may only be called once.");
+    public void setFlags(int flags) {
+        try {
+            mBinder.setFlags(flags);
+        } catch (RemoteException e) {
+            Log.wtf(TAG, "Failure in setFlags.", e);
+        }
+    }
+
+    /**
+     * Set if this session is currently active and ready to receive commands. If
+     * set to false your session's controller may not be discoverable. You must
+     * set the session to active before it can start receiving media button
+     * events or transport commands.
+     *
+     * @param active Whether this session is active or not.
+     */
+    public void setActive(boolean active) {
+        if (mActive == active) {
+            return;
         }
         try {
-            mBinder.publish();
+            mBinder.setActive(active);
+            mActive = active;
         } catch (RemoteException e) {
-            Log.wtf(TAG, "Failure in publish.", e);
+            Log.wtf(TAG, "Failure in setActive.", e);
         }
-        mPublished = true;
+    }
+
+    /**
+     * Get the current active state of this session.
+     *
+     * @return True if the session is active, false otherwise.
+     */
+    public boolean isActive() {
+        return mActive;
     }
 
     /**
diff --git a/media/java/android/media/session/SessionManager.java b/media/java/android/media/session/SessionManager.java
index 15bf0e3..fd022fc 100644
--- a/media/java/android/media/session/SessionManager.java
+++ b/media/java/android/media/session/SessionManager.java
@@ -16,11 +16,13 @@
 
 package android.media.session;
 
+import android.content.ComponentName;
 import android.content.Context;
 import android.media.session.ISessionManager;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.service.notification.NotificationListenerService;
 import android.util.Log;
 
 import java.util.ArrayList;
@@ -79,12 +81,27 @@
     /**
      * Get a list of controllers for all ongoing sessions. This requires the
      * android.Manifest.permission.MEDIA_CONTENT_CONTROL permission be held by
-     * the calling app.
+     * the calling app. You may also retrieve this list if your app is an
+     * enabled notification listener using the
+     * {@link NotificationListenerService} APIs, in which case you must pass the
+     * {@link ComponentName} of your enabled listener.
      *
-     * @return a list of controllers for ongoing sessions
+     * @param notificationListener The enabled notification listener component.
+     *            May be null.
+     * @return A list of controllers for ongoing sessions
      */
-    public List<SessionController> getActiveSessions() {
-        // TODO
-        return new ArrayList<SessionController>();
+    public List<SessionController> getActiveSessions(ComponentName notificationListener) {
+        ArrayList<SessionController> controllers = new ArrayList<SessionController>();
+        try {
+            List<IBinder> binders = mService.getSessions(notificationListener);
+            for (int i = binders.size() - 1; i >= 0; i--) {
+                SessionController controller = SessionController.fromBinder(ISessionController.Stub
+                        .asInterface(binders.get(i)));
+                controllers.add(controller);
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "Failed to get active sessions: ", e);
+        }
+        return controllers;
     }
 }
diff --git a/packages/SystemUI/res/drawable-hdpi/ic_lock_24dp.png b/packages/SystemUI/res/drawable-hdpi/ic_lock_24dp.png
new file mode 100644
index 0000000..c779437
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/ic_lock_24dp.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/ic_lock_24dp.png b/packages/SystemUI/res/drawable-mdpi/ic_lock_24dp.png
new file mode 100644
index 0000000..98ba690
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/ic_lock_24dp.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_lock_24dp.png b/packages/SystemUI/res/drawable-xhdpi/ic_lock_24dp.png
new file mode 100644
index 0000000..61947ea
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/ic_lock_24dp.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/ic_lock_24dp.png b/packages/SystemUI/res/drawable-xxhdpi/ic_lock_24dp.png
new file mode 100644
index 0000000..0b563b1
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xxhdpi/ic_lock_24dp.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxxhdpi/ic_lock_24dp.png b/packages/SystemUI/res/drawable-xxxhdpi/ic_lock_24dp.png
new file mode 100644
index 0000000..3600ee6
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xxxhdpi/ic_lock_24dp.png
Binary files differ
diff --git a/packages/SystemUI/res/layout-sw600dp/heads_up.xml b/packages/SystemUI/res/layout-sw600dp/heads_up.xml
index 71f7c21..f7035fe 100644
--- a/packages/SystemUI/res/layout-sw600dp/heads_up.xml
+++ b/packages/SystemUI/res/layout-sw600dp/heads_up.xml
@@ -25,7 +25,6 @@
         android:id="@+id/content_holder"
         android:layout_height="wrap_content"
         android:layout_width="@dimen/notification_panel_width"
-        android:layout_marginStart="@dimen/notification_panel_margin_left"
         android:background="@drawable/heads_up_window_bg"
         />
 </com.android.systemui.statusbar.policy.HeadsUpNotificationView>
diff --git a/packages/SystemUI/res/layout/flip_settings.xml b/packages/SystemUI/res/layout/flip_settings.xml
index f3c1b90..28d9625 100644
--- a/packages/SystemUI/res/layout/flip_settings.xml
+++ b/packages/SystemUI/res/layout/flip_settings.xml
@@ -22,5 +22,4 @@
     android:layout_height="wrap_content"
     android:background="#5f000000"
     android:animateLayoutChanges="true"
-    android:visibility="gone"
     android:columnCount="@integer/quick_settings_num_columns" />
\ 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 3a58b84..e4954e7 100644
--- a/packages/SystemUI/res/layout/heads_up.xml
+++ b/packages/SystemUI/res/layout/heads_up.xml
@@ -22,6 +22,5 @@
         android:layout_height="wrap_content"
         android:layout_width="@dimen/notification_panel_width"
         android:id="@+id/content_holder"
-        android:layout_marginStart="@dimen/notification_panel_margin_left"
         android:background="@drawable/notification_panel_bg"
         />
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/keyguard_bottom_area.xml b/packages/SystemUI/res/layout/keyguard_bottom_area.xml
index ec5acba..194829d 100644
--- a/packages/SystemUI/res/layout/keyguard_bottom_area.xml
+++ b/packages/SystemUI/res/layout/keyguard_bottom_area.xml
@@ -55,4 +55,14 @@
         android:textStyle="italic"
         android:textAppearance="?android:attr/textAppearanceMedium"/>
 
+    <ImageView
+        android:layout_width="64dp"
+        android:layout_height="64dp"
+        android:layout_gravity="bottom|center_horizontal"
+        android:src="@drawable/ic_lock_24dp"
+        android:scaleType="center"
+        android:alpha="0.7"
+        android:layerType="hardware"
+        android:tint="#ffffffff"/>
+
 </com.android.systemui.statusbar.phone.KeyguardBottomAreaView>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index 761ad42..3267c36 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -23,9 +23,7 @@
     xmlns:systemui="http://schemas.android.com/apk/res/com.android.systemui"
     android:id="@+id/notification_panel"
     android:layout_width="0dp"
-    android:layout_height="wrap_content"
-    android:paddingTop="@dimen/notification_panel_padding_top"
-    android:layout_marginStart="@dimen/notification_panel_margin_left"
+    android:layout_height="match_parent"
     >
 
     <include
@@ -36,15 +34,6 @@
         android:layout_gravity="bottom"
         />
 
-    <include
-        layout="@layout/status_bar_flip_button"
-        android:id="@+id/keyguard_flipper"
-        android:layout_width="50dp"
-        android:layout_height="50dp"
-        android:layout_gravity="right|top"
-        android:layout_marginTop="@dimen/status_bar_height"
-        android:visibility="gone" />
-
     <com.android.keyguard.CarrierText
         android:id="@+id/keyguard_carrier_text"
         android:layout_width="wrap_content"
@@ -54,11 +43,6 @@
         android:ellipsize="marquee"
         android:textAppearance="?android:attr/textAppearanceMedium" />
 
-    <include layout="@layout/status_bar_expanded_header"
-        android:layout_width="match_parent"
-        android:layout_height="@dimen/notification_panel_header_height"
-        />
-
     <include
         layout="@layout/keyguard_status_view"
         android:layout_height="wrap_content"
@@ -74,27 +58,54 @@
         android:visibility="gone"
         />
 
-    <FrameLayout
+    <com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer
         android:id="@+id/notification_container_parent"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:layout_marginBottom="@dimen/close_handle_underlap"
-        >
-        <include
-            layout="@layout/flip_settings"
-            android:layout_marginTop="@dimen/notification_panel_header_height"
+        android:clipToPadding="false"
+        android:clipChildren="false">
+
+        <com.android.systemui.statusbar.phone.ObservableScrollView
+            android:id="@+id/scroll_view"
             android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            />
+            android:layout_height="match_parent"
+            android:visibility="invisible"
+            android:scrollbars="none"
+            android:fillViewport="true">
+            <LinearLayout
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:orientation="vertical">
+                <include
+                    layout="@layout/flip_settings"
+                    android:layout_marginTop="@dimen/status_bar_header_height_expanded"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"/>
+
+                <!-- A view to reserve space for the collapsed stack -->
+                <View
+                    android:layout_height="@dimen/collapsed_stack_height"
+                    android:layout_width="match_parent"/>
+            </LinearLayout>
+        </com.android.systemui.statusbar.phone.ObservableScrollView>
+
 
         <com.android.systemui.statusbar.stack.NotificationStackScrollLayout
             android:id="@+id/notification_stack_scroller"
             android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            />
-    </FrameLayout>
+            android:layout_height="match_parent"
+            android:layout_marginBottom="@dimen/close_handle_underlap"/>
+
+    </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/keyguard_bottom_area"
         android:visibility="gone" />
+
 </com.android.systemui.statusbar.phone.NotificationPanelView><!-- end of sliding panel -->
diff --git a/packages/SystemUI/res/layout/status_bar_expanded_header.xml b/packages/SystemUI/res/layout/status_bar_expanded_header.xml
index 8975728..460dd4b 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded_header.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded_header.xml
@@ -15,21 +15,30 @@
 ** limitations under the License.
 -->
 
-<LinearLayout
+<!-- Extends RelativeLayout -->
+<com.android.systemui.statusbar.phone.StatusBarHeaderView
     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"
-    android:layout_height="@dimen/notification_panel_header_height"
-    android:background="@drawable/notification_header_bg"
+    android:layout_height="@dimen/status_bar_header_height"
     android:orientation="horizontal"
     android:gravity="center_vertical"
     android:baselineAligned="false"
     >
+
+    <View
+        android:id="@+id/background"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:background="@drawable/notification_header_bg"
+        android:clickable="true"
+        />
     <RelativeLayout
         android:id="@+id/datetime"
         android:layout_width="wrap_content"
         android:layout_height="match_parent"
+        android:layout_gravity="start"
         android:paddingStart="8dp"
         android:paddingEnd="8dp"
         android:background="@drawable/ic_notify_button_bg"
@@ -55,12 +64,6 @@
             />
     </RelativeLayout>
 
-    <Space
-        android:layout_width="0dp"
-        android:layout_height="0dp"
-        android:layout_weight="1"
-        />
-
     <TextView
         android:id="@+id/header_debug_info"
         android:visibility="invisible"
@@ -74,18 +77,22 @@
         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"
         />
 
-    <include layout="@layout/status_bar_flip_button"
-        android:id="@+id/header_flipper"
-        android:layout_width="50dp"
-        android:layout_height="50dp"
-        android:layout_marginStart="12dp" />
-</LinearLayout>
+
+
+</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
index b7dff8c..f4d7033 100644
--- a/packages/SystemUI/res/layout/status_bar_flip_button.xml
+++ b/packages/SystemUI/res/layout/status_bar_flip_button.xml
@@ -15,22 +15,11 @@
   ~ limitations under the License
   -->
 
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
-        android:layout_width="50dp"
-        android:layout_height="50dp">
-    <ImageView android:id="@+id/settings_button"
-        android:layout_width="50dp"
-        android:layout_height="50dp"
-        android:scaleType="center"
-        android:src="@drawable/ic_notify_settings"
-        android:background="@drawable/ic_notify_button_bg"
-        android:contentDescription="@string/accessibility_desc_quick_settings" />
-    <ImageView android:id="@+id/notification_button"
-        android:layout_width="50dp"
-        android:layout_height="50dp"
-        android:scaleType="center"
-        android:src="@drawable/ic_notifications"
-        android:background="@drawable/ic_notify_button_bg"
-        android:visibility="gone"
-        android:contentDescription="@string/accessibility_notifications_button" />
-</FrameLayout>
\ No newline at end of file
+<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/super_status_bar.xml b/packages/SystemUI/res/layout/super_status_bar.xml
index 61d43d7..f9b022c 100644
--- a/packages/SystemUI/res/layout/super_status_bar.xml
+++ b/packages/SystemUI/res/layout/super_status_bar.xml
@@ -33,11 +33,10 @@
     <com.android.systemui.statusbar.phone.PanelHolder
         android:id="@+id/panel_holder"
         android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:layout_marginTop="@dimen/panel_holder_padding_top">
+        android:layout_height="match_parent" >
         <include layout="@layout/status_bar_expanded"
             android:layout_width="@dimen/notification_panel_width"
-            android:layout_height="wrap_content"
+            android:layout_height="match_parent"
             android:layout_gravity="start|top" />
     </com.android.systemui.statusbar.phone.PanelHolder>
 
diff --git a/packages/SystemUI/res/values-sw600dp-land/dimens.xml b/packages/SystemUI/res/values-sw600dp-land/dimens.xml
deleted file mode 100644
index c6c0719..0000000
--- a/packages/SystemUI/res/values-sw600dp-land/dimens.xml
+++ /dev/null
@@ -1,22 +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.
-*/
--->
-<resources>
-    <!-- Layout parameters for the notification panel -->
-	<dimen name="notification_panel_margin_bottom">0dp</dimen>
-    <dimen name="notification_panel_margin_left">32dp</dimen>
-</resources>
diff --git a/packages/SystemUI/res/values-sw600dp/dimens.xml b/packages/SystemUI/res/values-sw600dp/dimens.xml
index 92e3885..7372181 100644
--- a/packages/SystemUI/res/values-sw600dp/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp/dimens.xml
@@ -19,10 +19,6 @@
     <!-- The width of the notification panel window: 446 + 16 + 16 (padding in the bg drawable) -->
     <dimen name="notification_panel_width">478dp</dimen>
 
-    <!-- Layout parameters for the notification panel -->
-    <dimen name="notification_panel_margin_bottom">192dp</dimen>
-    <dimen name="notification_panel_margin_left">16dp</dimen>
-
     <!-- Gravity for the notification panel -->
     <!-- 0x31 = top|center_horizontal -->
     <integer name="notification_panel_layout_gravity">0x31</integer>
@@ -43,9 +39,6 @@
     <dimen name="status_bar_recents_thumbnail_width">200dp</dimen>
     <dimen name="status_bar_recents_thumbnail_height">177dp</dimen>
 
-    <!-- On tablets, panels drop from the statusbar instead of overlapping it. -->
-    <dimen name="panel_holder_padding_top">@*android:dimen/status_bar_height</dimen>
-
     <!-- Minimum fraction of the screen that should be taken up by the notification panel. -->
     <item type="dimen" name="notification_panel_min_height_frac">40%</item>
 
diff --git a/packages/SystemUI/res/values-sw720dp/dimens.xml b/packages/SystemUI/res/values-sw720dp/dimens.xml
index b1fc00a..a1c5b66 100644
--- a/packages/SystemUI/res/values-sw720dp/dimens.xml
+++ b/packages/SystemUI/res/values-sw720dp/dimens.xml
@@ -24,8 +24,6 @@
 
     <!-- The width of the ticker, including the icon -->
     <dimen name="notification_ticker_width">360dp</dimen>
-    <!-- Status bar panel bottom offset (height of status bar - overlap) -->
-    <dimen name="status_bar_panel_bottom_offset">36dp</dimen>
     <!-- gap on either side of status bar notification icons -->
     <dimen name="status_bar_icon_padding">1dp</dimen>
     <!-- The width of the notification panel window -->
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index c3ccb59..93a75bc 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -99,9 +99,6 @@
 
     <integer name="blinds_pop_duration_ms">10</integer>
 
-    <!-- The device supports quick settings. -->
-    <bool name="config_hasQuickSettings">true</bool>
-
     <!-- Should "4G" be shown instead of "LTE" when the network is NETWORK_TYPE_LTE? -->
     <bool name="config_show4GForLTE">true</bool>
 
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index d763bd6..8d3a565 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -152,21 +152,11 @@
     <!-- Amount of close_handle that will NOT overlap the notification list -->
     <dimen name="close_handle_underlap">32dp</dimen>
 
-    <!-- Height of the notification panel header bar -->
-    <dimen name="notification_panel_header_height">48dp</dimen>
+    <!-- Height of the status bar header bar -->
+    <dimen name="status_bar_header_height">48dp</dimen>
 
-    <!-- Extra space above the panel -->
-    <dimen name="notification_panel_padding_top">0dp</dimen>
-
-    <!-- Extra space above the clock in the panel -->
-    <dimen name="notification_panel_header_padding_top">0dp</dimen>
-
-    <!-- Extra space above the panel holder -->
-    <dimen name="panel_holder_padding_top">0dp</dimen>
-
-    <!-- Layout parameters for the notification panel -->
-    <dimen name="notification_panel_margin_bottom">0dp</dimen>
-    <dimen name="notification_panel_margin_left">0dp</dimen>
+    <!-- Height of the status bar header bar when expanded -->
+    <dimen name="status_bar_header_height_expanded">144dp</dimen>
 
     <!-- Gravity for the notification panel -->
     <!-- 0x37 = fill_horizontal|top -->
@@ -261,6 +251,9 @@
     <!-- The padding between the individual notification cards. -->
     <dimen name="notification_padding">3dp</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>
+
     <!-- Width of the zen mode interstitial dialog. -->
     <dimen name="zen_mode_dialog_width">320dp</dimen>
 
diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml
new file mode 100644
index 0000000..e5168c4
--- /dev/null
+++ b/packages/SystemUI/res/values/ids.xml
@@ -0,0 +1,30 @@
+<?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>
+    <item type="id" name="translation_y_animator_tag"/>
+    <item type="id" name="translation_z_animator_tag"/>
+    <item type="id" name="alpha_animator_tag"/>
+    <item type="id" name="top_inset_animator_tag"/>
+    <item type="id" name="height_animator_tag"/>
+    <item type="id" name="translation_y_animator_end_value_tag"/>
+    <item type="id" name="translation_z_animator_end_value_tag"/>
+    <item type="id" name="alpha_animator_end_value_tag"/>
+    <item type="id" name="top_inset_animator_end_value_tag"/>
+    <item type="id" name="height_animator_end_value_tag"/>
+</resources>
+
diff --git a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
index d38d828..6387a92 100644
--- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
@@ -322,6 +322,7 @@
         anim.addListener(new AnimatorListenerAdapter() {
             public void onAnimationEnd(Animator animator) {
                 updateAlphaFromOffset(animView, canAnimViewBeDismissed);
+                mCallback.onChildSnappedBack(animView);
             }
         });
         anim.start();
@@ -407,5 +408,7 @@
         void onChildDismissed(View v);
 
         void onDragCancelled(View v);
+
+        void onChildSnappedBack(View animView);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsHorizontalScrollView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsHorizontalScrollView.java
index 35c824b..0759b8e 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentsHorizontalScrollView.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsHorizontalScrollView.java
@@ -217,6 +217,10 @@
     public void onDragCancelled(View v) {
     }
 
+    @Override
+    public void onChildSnappedBack(View animView) {
+    }
+
     public View getChildAtPosition(MotionEvent ev) {
         final float x = ev.getX() + getScrollX();
         final float y = ev.getY() + getScrollY();
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsVerticalScrollView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsVerticalScrollView.java
index 297fe0d..c2dde6a 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentsVerticalScrollView.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsVerticalScrollView.java
@@ -225,6 +225,10 @@
     public void onDragCancelled(View v) {
     }
 
+    @Override
+    public void onChildSnappedBack(View animView) {
+    }
+
     public View getChildAtPosition(MotionEvent ev) {
         final float x = ev.getX() + getScrollX();
         final float y = ev.getY() + getScrollY();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index 2c7464a..a325186 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -302,7 +302,7 @@
         ArrayList<StatusBarNotification> notifications = new ArrayList<StatusBarNotification>();
         mCommandQueue = new CommandQueue(this, iconList);
 
-        int[] switches = new int[7];
+        int[] switches = new int[8];
         ArrayList<IBinder> binders = new ArrayList<IBinder>();
         try {
             mBarService.registerStatusBar(mCommandQueue, iconList, notificationKeys, notifications,
@@ -317,7 +317,7 @@
         setSystemUiVisibility(switches[1], 0xffffffff);
         topAppWindowChanged(switches[2] != 0);
         // StatusBarManagerService has a back up of IME token and it's restored here.
-        setImeWindowStatus(binders.get(0), switches[3], switches[4]);
+        setImeWindowStatus(binders.get(0), switches[3], switches[4], switches[7] != 0);
         setHardKeyboardStatus(switches[5] != 0, switches[6] != 0);
 
         // Set up the initial icon state
@@ -1144,7 +1144,6 @@
     protected abstract void updateNotificationIcons();
     protected abstract void tick(IBinder key, StatusBarNotification n, boolean firstTime);
     protected abstract void updateExpandedViewPos(int expandedPosition);
-    protected abstract int getExpandedViewMaxHeight();
     protected abstract boolean shouldDisableNavbarGestures();
 
     protected boolean isTopNotification(ViewGroup parent, NotificationData.Entry entry) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index bbbe8fa..5362af5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -65,6 +65,8 @@
     public static final int FLAG_EXCLUDE_INPUT_METHODS_PANEL = 1 << 3;
     public static final int FLAG_EXCLUDE_COMPAT_MODE_PANEL = 1 << 4;
 
+    private static final String SHOW_IME_SWITCHER_KEY = "showImeSwitcherKey";
+
     private StatusBarIconList mList;
     private Callbacks mCallbacks;
     private Handler mHandler = new H();
@@ -91,7 +93,8 @@
         public void animateExpandSettingsPanel();
         public void setSystemUiVisibility(int vis, int mask);
         public void topAppWindowChanged(boolean visible);
-        public void setImeWindowStatus(IBinder token, int vis, int backDisposition);
+        public void setImeWindowStatus(IBinder token, int vis, int backDisposition,
+                boolean showImeSwitcher);
         public void setHardKeyboardStatus(boolean available, boolean enabled);
         public void toggleRecentApps();
         public void preloadRecentApps();
@@ -190,11 +193,13 @@
         }
     }
 
-    public void setImeWindowStatus(IBinder token, int vis, int backDisposition) {
+    public void setImeWindowStatus(IBinder token, int vis, int backDisposition,
+            boolean showImeSwitcher) {
         synchronized (mList) {
             mHandler.removeMessages(MSG_SHOW_IME_BUTTON);
-            mHandler.obtainMessage(MSG_SHOW_IME_BUTTON, vis, backDisposition, token)
-                    .sendToTarget();
+            Message m = mHandler.obtainMessage(MSG_SHOW_IME_BUTTON, vis, backDisposition, token);
+            m.getData().putBoolean(SHOW_IME_SWITCHER_KEY, showImeSwitcher);
+            m.sendToTarget();
         }
     }
 
@@ -298,7 +303,8 @@
                     mCallbacks.topAppWindowChanged(msg.arg1 != 0);
                     break;
                 case MSG_SHOW_IME_BUTTON:
-                    mCallbacks.setImeWindowStatus((IBinder) msg.obj, msg.arg1, msg.arg2);
+                    mCallbacks.setImeWindowStatus((IBinder) msg.obj, msg.arg1, msg.arg2,
+                            msg.getData().getBoolean(SHOW_IME_SWITCHER_KEY, false));
                     break;
                 case MSG_SET_HARD_KEYBOARD_STATUS:
                     mCallbacks.setHardKeyboardStatus(msg.arg1 != 0, msg.arg2 != 0);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
index 33e9051..169521d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
@@ -17,11 +17,6 @@
 package com.android.systemui.statusbar;
 
 import android.content.Context;
-import android.graphics.Canvas;
-import android.graphics.Outline;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.InsetDrawable;
 import android.util.AttributeSet;
 import android.view.MotionEvent;
 import android.view.View;
@@ -112,6 +107,10 @@
         mClipTopAmount = clipTopAmount;
     }
 
+    public int getClipTopAmount() {
+        return mClipTopAmount;
+    }
+
     public void setOnHeightChangedListener(OnHeightChangedListener listener) {
         mOnHeightChangedListener = listener;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
index 1f15eaf..379bd05 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
@@ -94,10 +94,6 @@
         updateClipping();
     }
 
-    public int getClipTopAmount() {
-        return mClipTopAmount;
-    }
-
     private void updateClipping() {
         mClipBounds.set(0, mClipTopAmount, getWidth(), mActualHeight);
         setClipBounds(mClipBounds);
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 627b80f..f63ba9c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -16,11 +16,17 @@
 
 package com.android.systemui.statusbar.phone;
 
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
 import android.content.Context;
 import android.util.AttributeSet;
 import android.view.MotionEvent;
+import android.view.VelocityTracker;
 import android.view.View;
 import android.view.accessibility.AccessibilityEvent;
+import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
 
 import com.android.systemui.R;
 import com.android.systemui.statusbar.ExpandableView;
@@ -29,18 +35,40 @@
 import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
 
 public class NotificationPanelView extends PanelView implements
-        ExpandableView.OnHeightChangedListener {
+        ExpandableView.OnHeightChangedListener, ObservableScrollView.Listener,
+        View.OnClickListener {
     public static final boolean DEBUG_GESTURES = true;
+    private static final int EXPANSION_ANIMATION_LENGTH = 375;
 
     PhoneStatusBar mStatusBar;
-    private View mHeader;
+    private StatusBarHeaderView mHeader;
+    private QuickSettingsContainerView mQsContainer;
     private View mKeyguardStatusView;
+    private ObservableScrollView mScrollView;
+    private View mStackScrollerContainer;
 
     private NotificationStackScrollLayout mNotificationStackScroller;
-    private boolean mTrackingSettings;
     private int mNotificationTopPadding;
     private boolean mAnimateNextTopPaddingChange;
 
+    private Interpolator mExpansionInterpolator;
+
+    private int mTrackingPointer;
+    private VelocityTracker mVelocityTracker;
+    private boolean mTracking;
+    private boolean mQsExpanded;
+    private float mInitialHeightOnTouch;
+    private float mInitialTouchX;
+    private float mInitialTouchY;
+    private float mQsExpansionHeight;
+    private int mQsMinExpansionHeight;
+    private int mQsMaxExpansionHeight;
+    private int mMinStackHeight;
+    private float mNotificationTranslation;
+    private int mStackScrollerIntrinsicPadding;
+    private boolean mQsExpansionEnabled = true;
+    private ValueAnimator mQsExpansionAnimator;
+
     public NotificationPanelView(Context context, AttributeSet attrs) {
         super(context, attrs);
     }
@@ -63,14 +91,21 @@
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
-
-        mHeader = findViewById(R.id.header);
+        mHeader = (StatusBarHeaderView) findViewById(R.id.header);
+        mHeader.getBackgroundView().setOnClickListener(this);
         mKeyguardStatusView = findViewById(R.id.keyguard_status_view);
+        mStackScrollerContainer = findViewById(R.id.notification_container_parent);
+        mQsContainer = (QuickSettingsContainerView) findViewById(R.id.quick_settings_container);
+        mScrollView = (ObservableScrollView) findViewById(R.id.scroll_view);
+        mScrollView.setListener(this);
         mNotificationStackScroller = (NotificationStackScrollLayout)
                 findViewById(R.id.notification_stack_scroller);
         mNotificationStackScroller.setOnHeightChangedListener(this);
         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);
     }
 
     @Override
@@ -78,11 +113,21 @@
         super.onLayout(changed, left, top, right, bottom);
         int keyguardBottomMargin =
                 ((MarginLayoutParams) mKeyguardStatusView.getLayoutParams()).bottomMargin;
-        mNotificationStackScroller.setTopPadding(mStatusBar.getBarState() == StatusBarState.KEYGUARD
-                ? mKeyguardStatusView.getBottom() + keyguardBottomMargin
-                : mHeader.getBottom() + mNotificationTopPadding,
-                mAnimateNextTopPaddingChange);
-        mAnimateNextTopPaddingChange = false;
+        if (!mQsExpanded) {
+            mStackScrollerIntrinsicPadding = mStatusBar.getBarState() == StatusBarState.KEYGUARD
+                    ? mKeyguardStatusView.getBottom() + keyguardBottomMargin
+                    : mHeader.getBottom() + mNotificationTopPadding;
+            mNotificationStackScroller.setTopPadding(mStackScrollerIntrinsicPadding,
+                    mAnimateNextTopPaddingChange);
+            mAnimateNextTopPaddingChange = false;
+        }
+
+        // Calculate quick setting heights.
+        mQsMinExpansionHeight = mHeader.getCollapsedHeight();
+        mQsMaxExpansionHeight = mHeader.getExpandedHeight() + mQsContainer.getHeight();
+        if (mQsExpansionHeight == 0) {
+            mQsExpansionHeight = mQsMinExpansionHeight;
+        }
     }
 
     public void animateNextTopPaddingChange() {
@@ -90,6 +135,30 @@
         requestLayout();
     }
 
+    /**
+     * @return Whether Quick Settings are currently expanded.
+     */
+    public boolean isQsExpanded() {
+        return mQsExpanded;
+    }
+
+    public void setQsExpansionEnabled(boolean qsExpansionEnabled) {
+        mQsExpansionEnabled = qsExpansionEnabled;
+        mHeader.setExpansionEnabled(qsExpansionEnabled);
+    }
+
+    public void closeQs() {
+        cancelAnimation();
+        setQsExpansion(mQsMinExpansionHeight);
+    }
+
+    public void openQs() {
+        cancelAnimation();
+        if (mQsExpansionEnabled) {
+            setQsExpansion(mQsMaxExpansionHeight);
+        }
+    }
+
     @Override
     public void fling(float vel, boolean always) {
         GestureRecorder gr = ((PhoneStatusBarView) mBar).mBar.getGestureRecorder();
@@ -114,42 +183,245 @@
 
     @Override
     public boolean onInterceptTouchEvent(MotionEvent event) {
-        // intercept for quick settings
-        if (event.getAction() == MotionEvent.ACTION_DOWN) {
-            final View target = mStatusBar.getBarState() == StatusBarState.KEYGUARD
-                    ? mKeyguardStatusView
-                    : mHeader;
-            final boolean inTarget = PhoneStatusBar.inBounds(target, event, true);
-            if (inTarget && !isInSettings()) {
-                mTrackingSettings = true;
-                requestDisallowInterceptTouchEvent(true);
-                return true;
-            }
-            if (!inTarget && isInSettings()) {
-                mTrackingSettings = true;
-                requestDisallowInterceptTouchEvent(true);
-                return true;
-            }
+        int pointerIndex = event.findPointerIndex(mTrackingPointer);
+        if (pointerIndex < 0) {
+            pointerIndex = 0;
+            mTrackingPointer = event.getPointerId(pointerIndex);
         }
-        return super.onInterceptTouchEvent(event);
+        final float x = event.getX(pointerIndex);
+        final float y = event.getY(pointerIndex);
+
+        switch (event.getActionMasked()) {
+            case MotionEvent.ACTION_DOWN:
+                mInitialTouchY = y;
+                mInitialTouchX = x;
+                initVelocityTracker();
+                trackMovement(event);
+                if (shouldIntercept(mInitialTouchX, mInitialTouchY, 0)) {
+                    getParent().requestDisallowInterceptTouchEvent(true);
+                }
+                break;
+            case MotionEvent.ACTION_POINTER_UP:
+                final int upPointer = event.getPointerId(event.getActionIndex());
+                if (mTrackingPointer == upPointer) {
+                    // gesture is ongoing, find a new pointer to track
+                    final int newIndex = event.getPointerId(0) != upPointer ? 0 : 1;
+                    mTrackingPointer = event.getPointerId(newIndex);
+                    mInitialTouchX = event.getX(newIndex);
+                    mInitialTouchY = event.getY(newIndex);
+                }
+                break;
+
+            case MotionEvent.ACTION_MOVE:
+                final float h = y - mInitialTouchY;
+                trackMovement(event);
+                if (Math.abs(h) > mTouchSlop && Math.abs(h) > Math.abs(x - mInitialTouchX)
+                        && shouldIntercept(mInitialTouchX, mInitialTouchY, h)) {
+                    onQsExpansionStarted();
+                    mInitialHeightOnTouch = mQsExpansionHeight;
+                    mInitialTouchY = y;
+                    mInitialTouchX = x;
+                    mTracking = true;
+                    return true;
+                }
+                break;
+        }
+        return !mQsExpanded && super.onInterceptTouchEvent(event);
     }
 
     @Override
     public boolean onTouchEvent(MotionEvent event) {
         // TODO: Handle doublefinger swipe to notifications again. Look at history for a reference
         // implementation.
-        if (mTrackingSettings) {
-            mStatusBar.onSettingsEvent(event);
-            if (event.getAction() == MotionEvent.ACTION_UP
-                    || event.getAction() == MotionEvent.ACTION_CANCEL) {
-                mTrackingSettings = false;
+        if (mTracking) {
+            int pointerIndex = event.findPointerIndex(mTrackingPointer);
+            if (pointerIndex < 0) {
+                pointerIndex = 0;
+                mTrackingPointer = event.getPointerId(pointerIndex);
+            }
+            final float y = event.getY(pointerIndex);
+            final float x = event.getX(pointerIndex);
+
+            switch (event.getActionMasked()) {
+                case MotionEvent.ACTION_DOWN:
+                    mTracking = true;
+                    mInitialTouchY = y;
+                    mInitialTouchX = x;
+                    onQsExpansionStarted();
+                    mInitialHeightOnTouch = mQsExpansionHeight;
+                    initVelocityTracker();
+                    trackMovement(event);
+                    break;
+
+                case MotionEvent.ACTION_POINTER_UP:
+                    final int upPointer = event.getPointerId(event.getActionIndex());
+                    if (mTrackingPointer == upPointer) {
+                        // gesture is ongoing, find a new pointer to track
+                        final int newIndex = event.getPointerId(0) != upPointer ? 0 : 1;
+                        final float newY = event.getY(newIndex);
+                        final float newX = event.getX(newIndex);
+                        mTrackingPointer = event.getPointerId(newIndex);
+                        mInitialHeightOnTouch = mQsExpansionHeight;
+                        mInitialTouchY = newY;
+                        mInitialTouchX = newX;
+                    }
+                    break;
+
+                case MotionEvent.ACTION_MOVE:
+                    final float h = y - mInitialTouchY;
+                    setQsExpansion(h + mInitialHeightOnTouch);
+                    trackMovement(event);
+                    break;
+
+                case MotionEvent.ACTION_UP:
+                case MotionEvent.ACTION_CANCEL:
+                    mTracking = false;
+                    mTrackingPointer = -1;
+                    trackMovement(event);
+
+                    float vel = getCurrentVelocity();
+
+                    // TODO: Better logic whether we should expand or not.
+                    flingSettings(vel, vel > 0);
+
+                    if (mVelocityTracker != null) {
+                        mVelocityTracker.recycle();
+                        mVelocityTracker = null;
+                    }
+                    break;
             }
             return true;
         }
-        if (isInSettings()) {
-            return true;
+
+        // Consume touch events when QS are expanded.
+        return mQsExpanded || super.onTouchEvent(event);
+    }
+
+    private void onQsExpansionStarted() {
+        cancelAnimation();
+
+        // Reset scroll position and apply that position to the expanded height.
+        float height = mQsExpansionHeight - mScrollView.getScrollY();
+        mScrollView.scrollTo(0, 0);
+        setQsExpansion(height);
+    }
+
+    private void expandQs() {
+        mHeader.setExpanded(true);
+        mNotificationStackScroller.setEnabled(false);
+        mScrollView.setVisibility(View.VISIBLE);
+        mQsExpanded = true;
+    }
+
+    private void collapseQs() {
+        mHeader.setExpanded(false);
+        mNotificationStackScroller.setEnabled(true);
+        mScrollView.setVisibility(View.INVISIBLE);
+        mQsExpanded = false;
+    }
+
+    private void setQsExpansion(float height) {
+        height = Math.min(Math.max(height, mQsMinExpansionHeight), mQsMaxExpansionHeight);
+        if (height > mQsMinExpansionHeight && !mQsExpanded) {
+            expandQs();
+        } else if (height <= mQsMinExpansionHeight && mQsExpanded) {
+            collapseQs();
         }
-        return super.onTouchEvent(event);
+        mQsExpansionHeight = height;
+        mHeader.setExpansion(height);
+        setQsTranslation(height);
+        setQsStackScrollerPadding(height);
+    }
+
+    private void setQsTranslation(float height) {
+        mQsContainer.setY(height - mQsContainer.getHeight());
+    }
+
+    private void setQsStackScrollerPadding(float height) {
+        float start = height - mScrollView.getScrollY() + mNotificationTopPadding;
+        float stackHeight = mNotificationStackScroller.getHeight() - start;
+        if (stackHeight <= mMinStackHeight) {
+            float overflow = mMinStackHeight - stackHeight;
+            stackHeight = mMinStackHeight;
+            start = mNotificationStackScroller.getHeight() - stackHeight;
+            mNotificationStackScroller.setTranslationY(overflow);
+            mNotificationTranslation = overflow + mScrollView.getScrollY();
+        } else {
+            mNotificationStackScroller.setTranslationY(0);
+            mNotificationTranslation = mScrollView.getScrollY();
+        }
+        mNotificationStackScroller.setTopPadding(clampQsStackScrollerPadding((int) start), false);
+    }
+
+    private int clampQsStackScrollerPadding(int desiredPadding) {
+        return Math.max(desiredPadding, mStackScrollerIntrinsicPadding);
+    }
+
+    private void trackMovement(MotionEvent event) {
+        if (mVelocityTracker != null) mVelocityTracker.addMovement(event);
+    }
+
+    private void initVelocityTracker() {
+        if (mVelocityTracker != null) {
+            mVelocityTracker.recycle();
+        }
+        mVelocityTracker = VelocityTracker.obtain();
+    }
+
+    private float getCurrentVelocity() {
+        if (mVelocityTracker == null) {
+            return 0;
+        }
+        mVelocityTracker.computeCurrentVelocity(1000);
+        return mVelocityTracker.getYVelocity();
+    }
+
+    private void cancelAnimation() {
+        if (mQsExpansionAnimator != null) {
+            mQsExpansionAnimator.cancel();
+        }
+    }
+    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);
+        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+            @Override
+            public void onAnimationUpdate(ValueAnimator animation) {
+                setQsExpansion((Float) animation.getAnimatedValue());
+            }
+        });
+        animator.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                mQsExpansionAnimator = null;
+            }
+        });
+        animator.start();
+        mQsExpansionAnimator = animator;
+    }
+
+    /**
+     * @return Whether we should intercept a gesture to open Quick Settings.
+     */
+    private boolean shouldIntercept(float x, float y, float yDiff) {
+        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();
+        if (mQsExpanded) {
+            return onHeader || (mScrollView.isScrolledToBottom() && yDiff < 0);
+        } else {
+            return onHeader;
+        }
     }
 
     @Override
@@ -164,14 +436,16 @@
     protected int getMaxPanelHeight() {
         if (!isInSettings()) {
             int maxPanelHeight = super.getMaxPanelHeight();
-            int emptyBottomMargin = mNotificationStackScroller.getEmptyBottomMargin();
+            int notificationMarginBottom = mStackScrollerContainer.getPaddingBottom();
+            int emptyBottomMargin = notificationMarginBottom
+                    + mNotificationStackScroller.getEmptyBottomMargin();
             return maxPanelHeight - emptyBottomMargin;
         }
         return super.getMaxPanelHeight();
     }
 
     private boolean isInSettings() {
-        return mStatusBar != null && mStatusBar.isFlippedToSettings();
+        return mQsExpanded;
     }
 
     @Override
@@ -200,4 +474,24 @@
     public void onHeightChanged(ExpandableView view) {
         requestPanelHeightUpdate();
     }
+
+    @Override
+    public void onScrollChanged() {
+        if (mQsExpanded) {
+            mNotificationStackScroller.setTranslationY(
+                    mNotificationTranslation - mScrollView.getScrollY());
+        }
+    }
+
+    @Override
+    public void onClick(View v) {
+        if (v == mHeader.getBackgroundView()) {
+            onQsExpansionStarted();
+            if (mQsExpanded) {
+                flingSettings(0 /* vel */, false /* expand */);
+            } else if (mQsExpansionEnabled) {
+                flingSettings(0 /* vel */, true /* expand */);
+            }
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java
new file mode 100644
index 0000000..f41e78d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java
@@ -0,0 +1,39 @@
+/*
+ * 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.graphics.Rect;
+import android.util.AttributeSet;
+import android.widget.FrameLayout;
+
+/**
+ * The container with notification stack scroller and quick settings inside.
+ */
+public class NotificationsQuickSettingsContainer extends FrameLayout {
+
+    public NotificationsQuickSettingsContainer(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    @Override
+    protected boolean fitSystemWindows(Rect insets) {
+        setPadding(0, 0, 0, insets.bottom);
+        insets.bottom = 0;
+        return true;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ObservableScrollView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ObservableScrollView.java
new file mode 100644
index 0000000..46484f3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ObservableScrollView.java
@@ -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
+ */
+
+package com.android.systemui.statusbar.phone;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.ScrollView;
+
+/**
+ * A scroll view which can be observed for scroll change events.
+ */
+public class ObservableScrollView extends ScrollView {
+
+    private Listener mListener;
+
+    public ObservableScrollView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public void setListener(Listener listener) {
+        mListener = listener;
+    }
+
+    public boolean isScrolledToBottom() {
+        return getScrollY() == getMaxScrollY();
+    }
+
+    private int getMaxScrollY() {
+        int scrollRange = 0;
+        if (getChildCount() > 0) {
+            View child = getChildAt(0);
+            scrollRange = Math.max(0,
+                    child.getHeight() - (getHeight() - mPaddingBottom - mPaddingTop));
+        }
+        return scrollRange;
+    }
+
+    @Override
+    protected void onScrollChanged(int l, int t, int oldl, int oldt) {
+        super.onScrollChanged(l, t, oldl, oldt);
+        if (mListener != null) {
+            mListener.onScrollChanged();
+        }
+    }
+
+    public interface Listener {
+        void onScrollChanged();
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
index 0cdca66..8c70517 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
@@ -75,7 +75,7 @@
     private boolean mClosing;
     private boolean mTracking;
     private int mTrackingPointer;
-    private int mTouchSlop;
+    protected int mTouchSlop;
 
     private TimeAnimator mTimeAnimator;
     private ObjectAnimator mPeekAnimator;
@@ -220,9 +220,9 @@
     private float mVel, mAccel;
     protected int mMaxPanelHeight = 0;
     private String mViewName;
-    protected float mInitialTouchY;
-    protected float mInitialTouchX;
-    protected float mFinalTouchY;
+    private float mInitialTouchY;
+    private float mInitialTouchX;
+    private float mFinalTouchY;
 
     protected void onExpandingFinished() {
     }
@@ -526,11 +526,14 @@
 
         switch (event.getActionMasked()) {
             case MotionEvent.ACTION_DOWN:
+                if (mTimeAnimator.isRunning()) {
+                    mTimeAnimator.cancel(); // end any outstanding animations
+                    return true;
+                }
                 mInitialTouchY = y;
                 mInitialTouchX = x;
                 initVelocityTracker();
                 trackMovement(event);
-                mTimeAnimator.cancel(); // end any outstanding animations
                 break;
             case MotionEvent.ACTION_POINTER_UP:
                 final int upPointer = event.getPointerId(event.getActionIndex());
@@ -569,7 +572,7 @@
     }
 
     protected boolean isScrolledToBottom() {
-        return false;
+        return true;
     }
 
     protected float getContentHeight() {
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 f945c79..c4ee6ff 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -73,7 +73,6 @@
 import android.view.MotionEvent;
 import android.view.VelocityTracker;
 import android.view.View;
-import android.view.ViewConfiguration;
 import android.view.ViewGroup;
 import android.view.ViewGroup.LayoutParams;
 import android.view.ViewPropertyAnimator;
@@ -192,8 +191,6 @@
     int mIconHPadding = -1;
     Display mDisplay;
     Point mCurrentDisplaySize = new Point();
-    private float mHeadsUpVerticalOffset;
-    private int[] mStackScrollerPosition = new int[2];
 
     StatusBarWindowView mStatusBarWindow;
     PhoneStatusBarView mStatusBarView;
@@ -222,14 +219,13 @@
     NotificationPanelView mNotificationPanel; // the sliding/resizing panel within the notification window
     View mExpandedContents;
     int mNotificationPanelGravity;
-    int mNotificationPanelMarginBottomPx, mNotificationPanelMarginPx;
+    int mNotificationPanelMarginBottomPx;
     float mNotificationPanelMinHeightFrac;
     boolean mNotificationPanelIsFullScreenWidth;
     TextView mNotificationPanelDebugText;
 
     // settings
     QuickSettings mQS;
-    boolean mHasQuickSettings;
     View mFlipSettingsView;
     QuickSettingsContainerView mSettingsContainer;
 
@@ -245,14 +241,14 @@
     int mKeyguardMaxNotificationCount;
     View mDateTimeView;
     View mClearButton;
-    FlipperButton mHeaderFlipper, mKeyguardFlipper;
+    ImageView mHeaderFlipper;
 
     // carrier/wifi label
     private TextView mCarrierLabel;
     private boolean mCarrierLabelVisible = false;
     private int mCarrierLabelHeight;
     private TextView mEmergencyCallLabel;
-    private int mNotificationHeaderHeight;
+    private int mStatusBarHeaderHeight;
     private View mKeyguardCarrierLabel;
 
     private boolean mShowCarrierInPanel = false;
@@ -326,11 +322,12 @@
             if (MULTIUSER_DEBUG) Log.d(TAG, String.format("User setup changed: " +
                     "selfChange=%s userSetup=%s mUserSetup=%s",
                     selfChange, userSetup, mUserSetup));
-            mHeaderFlipper.userSetup(userSetup);
-            mKeyguardFlipper.userSetup(userSetup);
 
             if (userSetup != mUserSetup) {
                 mUserSetup = userSetup;
+                if (mNotificationPanel != null) {
+                    mNotificationPanel.setQsExpansionEnabled(isDeviceProvisioned() && userSetup);
+                }
                 if (!mUserSetup && mStatusBarView != null)
                     animateCollapseQuickSettings();
             }
@@ -651,16 +648,13 @@
         mClearButton.setEnabled(false);
         mDateView = (DateView)mStatusBarWindow.findViewById(R.id.date);
 
-        mHasQuickSettings = res.getBoolean(R.bool.config_hasQuickSettings);
-
         mDateTimeView = mNotificationPanelHeader.findViewById(R.id.datetime);
         if (mDateTimeView != null) {
             mDateTimeView.setOnClickListener(mClockClickListener);
             mDateTimeView.setEnabled(true);
         }
 
-        mHeaderFlipper = new FlipperButton(mStatusBarWindow.findViewById(R.id.header_flipper));
-        mKeyguardFlipper =new FlipperButton(mStatusBarWindow.findViewById(R.id.keyguard_flipper));
+        mHeaderFlipper = (ImageView) mStatusBarWindow.findViewById(R.id.header_flipper);
 
         if (!mNotificationPanelIsFullScreenWidth) {
             mNotificationPanel.setSystemUiVisibility(
@@ -735,26 +729,23 @@
 //                    updateCarrierLabelVisibility(false);
         }
 
-        // Quick Settings (where available, some restrictions apply)
-        if (mHasQuickSettings) {
-            // Quick Settings needs a container to survive
-            mSettingsContainer = (QuickSettingsContainerView)
-                    mStatusBarWindow.findViewById(R.id.quick_settings_container);
-            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,
-                        mLocationController, mRotationLockController);
-            } else {
-                mQS = null; // fly away, be free
+        // Quick Settings needs a container to survive
+        mSettingsContainer = (QuickSettingsContainerView)
+                mStatusBarWindow.findViewById(R.id.quick_settings_container);
+        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,
+                    mLocationController, mRotationLockController);
+        } else {
+            mQS = null; // fly away, be free
         }
 
         PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
@@ -775,101 +766,6 @@
         return mStatusBarView;
     }
 
-    public boolean onSettingsEvent(MotionEvent event) {
-        userActivity();
-        if (mSettingsClosing
-                && mFlipSettingsViewAnim != null && mFlipSettingsViewAnim.isRunning()) {
-            return true;
-        }
-        if (mSettingsTracker != null) {
-            mSettingsTracker.addMovement(event);
-        }
-        final int slop = ViewConfiguration.get(mContext).getScaledTouchSlop();
-        if (event.getAction() == MotionEvent.ACTION_DOWN) {
-            mSettingsTracker = VelocityTracker.obtain();
-            mSettingsDownY = event.getY();
-            mSettingsCancelled = false;
-            mSettingsStarted = false;
-            mSettingsClosing = mFlipSettingsView.getVisibility() == View.VISIBLE;
-            if (mSettingsClosing) {
-                mStackScroller.setVisibility(View.VISIBLE);
-            } else {
-                mFlipSettingsView.setTranslationY(-mNotificationPanel.getMeasuredHeight());
-            }
-            dispatchSettingsEvent(event);
-        } else if (mSettingsTracker != null && (event.getAction() == MotionEvent.ACTION_UP
-                || event.getAction() == MotionEvent.ACTION_CANCEL)) {
-            final float dy = event.getY() - mSettingsDownY;
-            final FlipperButton flipper = mState == StatusBarState.KEYGUARD
-                    ? mKeyguardFlipper
-                    : mHeaderFlipper;
-            final boolean inButton = flipper.inHolderBounds(event);
-            final boolean qsTap = mSettingsClosing && Math.abs(dy) < slop;
-            if (!qsTap && !inButton) {
-                mSettingsTracker.computeCurrentVelocity(1000);
-                final float vy = mSettingsTracker.getYVelocity();
-                final boolean animate = true;
-                if (dy <= slop || vy <= 0) {
-                    flipToNotifications(animate);
-                } else {
-                    flipToSettings(animate);
-                }
-            }
-            mSettingsTracker.recycle();
-            mSettingsTracker = null;
-            dispatchSettingsEvent(event);
-        } else if (mSettingsTracker != null && event.getAction() == MotionEvent.ACTION_MOVE) {
-            final float dy = event.getY() - mSettingsDownY;
-            if (mSettingsClosing) {
-                positionSettings(dy);
-                final boolean qsTap = Math.abs(dy) < slop;
-                if (!mSettingsCancelled && !qsTap) {
-                    MotionEvent cancelEvent = MotionEvent.obtainNoHistory(event);
-                    cancelEvent.setAction(MotionEvent.ACTION_CANCEL);
-                    dispatchSettingsEvent(cancelEvent);
-                    mSettingsCancelled = true;
-                }
-            } else {
-                if (!mSettingsStarted && dy > slop) {
-                    mSettingsStarted = true;
-                    mFlipSettingsView.setVisibility(View.VISIBLE);
-                    mStackScroller.setVisibility(View.VISIBLE);
-                }
-                if (mSettingsStarted) {
-                    positionSettings(dy);
-                }
-                dispatchSettingsEvent(event);
-            }
-        }
-        return true;
-    }
-
-    private void dispatchSettingsEvent(MotionEvent event) {
-        final View target = mSettingsClosing ? mFlipSettingsView : mNotificationPanelHeader;
-        final int[] targetLoc = new int[2];
-        target.getLocationInWindow(targetLoc);
-        final int[] panelLoc = new int[2];
-        mNotificationPanel.getLocationInWindow(panelLoc);
-        final int dx = targetLoc[0] - panelLoc[0];
-        final int dy = targetLoc[1] - panelLoc[1];
-        event.offsetLocation(-dx, -dy);
-        target.dispatchTouchEvent(event);
-    }
-
-    private void positionSettings(float dy) {
-        if (mSettingsClosing) {
-            final int ph = mNotificationPanel.getMeasuredHeight();
-            dy = Math.min(Math.max(-ph, dy), 0);
-            mFlipSettingsView.setTranslationY(dy);
-            mStackScroller.setTranslationY(ph + dy);
-        } else {
-            final int h = mFlipSettingsView.getBottom();
-            dy = Math.min(Math.max(0, dy), h);
-            mFlipSettingsView.setTranslationY(-h + dy);
-            mStackScroller.setTranslationY(dy);
-        }
-    }
-
     private void startKeyguard() {
         KeyguardViewMediator keyguardViewMediator = getComponent(KeyguardViewMediator.class);
         mStatusBarKeyguardViewManager = keyguardViewMediator.registerStatusBar(this,
@@ -1230,8 +1126,11 @@
             ((ImageView)mClearButton).setImageResource(R.drawable.ic_notify_clear);
         }
 
-        mHeaderFlipper.refreshLayout();
-        mKeyguardFlipper.refreshLayout();
+        if (mHeaderFlipper != null) {
+            // Force asset reloading
+            mHeaderFlipper.setImageDrawable(null);
+            mHeaderFlipper.setImageResource(R.drawable.ic_notify_quicksettings);
+        }
 
         refreshAllStatusBarIcons();
     }
@@ -1286,8 +1185,7 @@
             }
         }
 
-        mHeaderFlipper.provisionCheck(provisioned);
-        mKeyguardFlipper.provisionCheck(provisioned);
+        mNotificationPanel.setQsExpansionEnabled(provisioned && mUserSetup);
     }
 
     @Override
@@ -1362,7 +1260,7 @@
         final boolean makeVisible =
             !(emergencyCallsShownElsewhere && mNetworkController.isEmergencyOnly())
             && mStackScroller.getHeight() < (mNotificationPanel.getHeight()
-                    - mCarrierLabelHeight - mNotificationHeaderHeight)
+                    - mCarrierLabelHeight - mStatusBarHeaderHeight)
             && mStackScroller.getVisibility() == View.VISIBLE
             && mState != StatusBarState.KEYGUARD;
 
@@ -1775,47 +1673,8 @@
     }
 
     public void flipToNotifications(boolean animate) {
-        cancelAnim(mFlipSettingsViewAnim);
-        cancelAnim(mScrollViewAnim);
-        cancelAnim(mClearButtonAnim);
-        mHeaderFlipper.cancel();
-        mKeyguardFlipper.cancel();
-        mStackScroller.setVisibility(View.VISIBLE);
-        final int h = mNotificationPanel.getMeasuredHeight();
-        if (animate) {
-            final float settingsY =
-                    mSettingsTracker != null ? mFlipSettingsView.getTranslationY() : 0;
-            final float scrollerY = mSettingsTracker != null ? mStackScroller.getTranslationY() : h;
-            mScrollViewAnim = start(
-                    interpolator(mDecelerateInterpolator,
-                        ObjectAnimator.ofFloat(mStackScroller, View.TRANSLATION_Y, scrollerY, 0)
-                            .setDuration(FLIP_DURATION)
-                        ));
-            mFlipSettingsViewAnim = start(
-                setVisibilityWhenDone(
-                    interpolator(mDecelerateInterpolator,
-                            ObjectAnimator.ofFloat(
-                                    mFlipSettingsView, View.TRANSLATION_Y, settingsY, -h))
-                        .setDuration(FLIP_DURATION),
-                    mFlipSettingsView, View.INVISIBLE));
-        } else {
-            mStackScroller.setTranslationY(0);
-            mFlipSettingsView.setTranslationY(-h);
-            mFlipSettingsView.setVisibility(View.INVISIBLE);
-        }
-        mHeaderFlipper.flipToNotifications(animate);
-        mKeyguardFlipper.flipToNotifications(animate);
-        mClearButton.setVisibility(View.VISIBLE);
-        mClearButton.setAlpha(0f);
-        setAreThereNotifications(); // this will show/hide the button as necessary
-        mNotificationPanel.postDelayed(new Runnable() {
-            public void run() {
-                updateCarrierLabelVisibility(false);
-            }
-        }, animate ? FLIP_DURATION - 150 : 0);
-        if (mOnFlipRunnable != null) {
-            mOnFlipRunnable.run();
-        }
+        // TODO: Animation
+        mNotificationPanel.closeQs();
     }
 
     @Override
@@ -1829,78 +1688,18 @@
         if (!mUserSetup) return;
 
         mNotificationPanel.expand();
-        if (mFlipSettingsView.getVisibility() != View.VISIBLE
-                || mFlipSettingsView.getTranslationY() < 0) {
-            flipToSettings(true /*animate*/);
-        }
+        mNotificationPanel.openQs();
 
         if (false) postStartTracing();
     }
 
     public boolean isFlippedToSettings() {
-        if (mFlipSettingsView != null) {
-            return mFlipSettingsView.getVisibility() == View.VISIBLE;
+        if (mNotificationPanel != null) {
+            return mNotificationPanel.isQsExpanded();
         }
         return false;
     }
 
-    public void flipToSettings(boolean animate) {
-        // Settings are not available in setup
-        if (!mUserSetup) return;
-
-        cancelAnim(mFlipSettingsViewAnim);
-        cancelAnim(mScrollViewAnim);
-        mHeaderFlipper.cancel();
-        mKeyguardFlipper.cancel();
-        cancelAnim(mClearButtonAnim);
-
-        mFlipSettingsView.setVisibility(View.VISIBLE);
-        final int h = mNotificationPanel.getMeasuredHeight();
-        if (animate) {
-            final float settingsY
-                    = mSettingsTracker != null ? mFlipSettingsView.getTranslationY() : -h;
-            final float scrollerY = mSettingsTracker != null ? mStackScroller.getTranslationY() : 0;
-            mFlipSettingsViewAnim = start(
-                    startDelay(0,
-                        interpolator(mDecelerateInterpolator,
-                            ObjectAnimator.ofFloat(mFlipSettingsView, View.TRANSLATION_Y,
-                                    settingsY, 0f)
-                                .setDuration(FLIP_DURATION)
-                            )));
-            mScrollViewAnim = start(
-                setVisibilityWhenDone(
-                    interpolator(mDecelerateInterpolator,
-                            ObjectAnimator.ofFloat(mStackScroller, View.TRANSLATION_Y, scrollerY, h)
-                            )
-                        .setDuration(FLIP_DURATION),
-                        mStackScroller, View.INVISIBLE));
-        } else {
-            mFlipSettingsView.setTranslationY(0);
-            mStackScroller.setTranslationY(h);
-            mStackScroller.setVisibility(View.INVISIBLE);
-        }
-        mHeaderFlipper.flipToSettings(animate);
-        mKeyguardFlipper.flipToSettings(animate);
-        if (animate) {
-            mClearButtonAnim = start(
-                setVisibilityWhenDone(
-                    ObjectAnimator.ofFloat(mClearButton, View.ALPHA, 0f)
-                    .setDuration(FLIP_DURATION),
-                    mClearButton, View.INVISIBLE));
-        } else {
-            mClearButton.setAlpha(0);
-            mClearButton.setVisibility(View.INVISIBLE);
-        }
-        mNotificationPanel.postDelayed(new Runnable() {
-            public void run() {
-                updateCarrierLabelVisibility(false);
-            }
-        }, animate ? FLIP_DURATION - 150 : 0);
-        if (mOnFlipRunnable != null) {
-            mOnFlipRunnable.run();
-        }
-    }
-
     public void animateCollapseQuickSettings() {
         mStatusBarView.collapseAllPanels(true);
     }
@@ -1927,12 +1726,10 @@
 
         mStackScroller.setVisibility(View.VISIBLE);
         mNotificationPanel.setVisibility(View.GONE);
-        mFlipSettingsView.setVisibility(View.GONE);
 
         setAreThereNotifications(); // show the clear button
 
-        mHeaderFlipper.reset();
-        mKeyguardFlipper.reset();
+        mNotificationPanel.closeQs();
 
         mExpandedVisible = false;
         if (mNavigationBarView != null)
@@ -2235,7 +2032,8 @@
     }
 
     @Override
-    public void setImeWindowStatus(IBinder token, int vis, int backDisposition) {
+    public void setImeWindowStatus(IBinder token, int vis, int backDisposition,
+            boolean showImeSwitcher) {
         boolean imeShown = (vis & InputMethodService.IME_VISIBLE) != 0;
         int flags = mNavigationIconHints;
         if ((backDisposition == InputMethodService.BACK_DISPOSITION_WILL_DISMISS) || imeShown) {
@@ -2243,7 +2041,7 @@
         } else {
             flags &= ~NAVIGATION_HINT_BACK_ALT;
         }
-        if (imeShown) {
+        if (showImeSwitcher) {
             flags |= NAVIGATION_HINT_IME_SHOWN;
         } else {
             flags &= ~NAVIGATION_HINT_IME_SHOWN;
@@ -2462,20 +2260,11 @@
         }
     }
 
-    void updateExpandedInvisiblePosition() {
-        mTrackingPosition = -mDisplayMetrics.heightPixels;
-    }
-
     static final float saturate(float a) {
         return a < 0f ? 0f : (a > 1f ? 1f : a);
     }
 
     @Override
-    protected int getExpandedViewMaxHeight() {
-        return mDisplayMetrics.heightPixels - mNotificationPanelMarginBottomPx;
-    }
-
-    @Override
     public void updateExpandedViewPos(int thingy) {
         if (SPEW) Log.v(TAG, "updateExpandedViewPos");
 
@@ -2485,15 +2274,8 @@
 
         FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mNotificationPanel.getLayoutParams();
         lp.gravity = mNotificationPanelGravity;
-        lp.setMarginStart(mNotificationPanelMarginPx);
         mNotificationPanel.setLayoutParams(lp);
 
-        if (ENABLE_HEADS_UP && mHeadsUpNotificationView != null) {
-            mHeadsUpNotificationView.setMargin(mNotificationPanelMarginPx);
-            mStackScroller.getLocationOnScreen(mStackScrollerPosition);
-            mHeadsUpVerticalOffset = mStackScrollerPosition[1] - mNaturalBarHeight;
-        }
-
         updateCarrierLabelVisibility(false);
     }
 
@@ -2541,17 +2323,6 @@
         animateCollapsePanels();
     }
 
-    private View.OnClickListener mSettingsButtonListener = new View.OnClickListener() {
-        public void onClick(View v) {
-            if (mHasQuickSettings) {
-                animateExpandSettingsPanel();
-            } else {
-                startActivityDismissingKeyguard(
-                        new Intent(android.provider.Settings.ACTION_SETTINGS), true);
-            }
-        }
-    };
-
     private View.OnClickListener mClockClickListener = new View.OnClickListener() {
         public void onClick(View v) {
             startActivityDismissingKeyguard(
@@ -2652,17 +2423,6 @@
         }
     }
 
-    public void animateHeadsUp(boolean animateInto, float frac) {
-        if (!ENABLE_HEADS_UP || mHeadsUpNotificationView == null) return;
-        frac = frac / 0.4f;
-        frac = frac < 1.0f ? frac : 1.0f;
-        float alpha = 1.0f - frac;
-        float offset = mHeadsUpVerticalOffset * frac;
-        offset = animateInto ? offset : 0f;
-        mHeadsUpNotificationView.setAlpha(alpha);
-        mHeadsUpNotificationView.setY(offset);
-    }
-
     public void onHeadsUpDismissed() {
         if (mInterruptingNotificationEntry == null) return;
         mHandler.sendEmptyMessage(MSG_HIDE_HEADS_UP);
@@ -2735,17 +2495,13 @@
 
         mFlingGestureMaxOutputVelocityPx = res.getDimension(R.dimen.fling_gesture_max_output_velocity);
 
-        mNotificationPanelMarginBottomPx
-            = (int) res.getDimension(R.dimen.notification_panel_margin_bottom);
-        mNotificationPanelMarginPx
-            = (int) res.getDimension(R.dimen.notification_panel_margin_left);
         mNotificationPanelGravity = res.getInteger(R.integer.notification_panel_layout_gravity);
         if (mNotificationPanelGravity <= 0) {
             mNotificationPanelGravity = Gravity.START | Gravity.TOP;
         }
 
         mCarrierLabelHeight = res.getDimensionPixelSize(R.dimen.carrier_label_height);
-        mNotificationHeaderHeight = res.getDimensionPixelSize(R.dimen.notification_panel_header_height);
+        mStatusBarHeaderHeight = res.getDimensionPixelSize(R.dimen.status_bar_header_height);
 
         mNotificationPanelMinHeightFrac = res.getFraction(R.dimen.notification_panel_min_height_frac, 1, 1);
         if (mNotificationPanelMinHeightFrac < 0f || mNotificationPanelMinHeightFrac > 1f) {
@@ -3015,9 +2771,6 @@
 
     private void updateKeyguardState() {
         if (mState == StatusBarState.KEYGUARD) {
-            if (isFlippedToSettings()) {
-                flipToNotifications(false /*animate*/);
-            }
             mKeyguardStatusView.setVisibility(View.VISIBLE);
             mKeyguardBottomArea.setVisibility(View.VISIBLE);
             mKeyguardIndicationTextView.setVisibility(View.VISIBLE);
@@ -3025,7 +2778,7 @@
             mKeyguardCarrierLabel.setVisibility(View.VISIBLE);
             mNotificationPanelHeader.setVisibility(View.GONE);
 
-            mKeyguardFlipper.setVisibility(View.VISIBLE);
+            mNotificationPanel.closeQs();
             mSettingsContainer.setKeyguardShowing(true);
         } else {
             mKeyguardStatusView.setVisibility(View.GONE);
@@ -3034,7 +2787,6 @@
             mKeyguardCarrierLabel.setVisibility(View.GONE);
             mNotificationPanelHeader.setVisibility(View.VISIBLE);
 
-            mKeyguardFlipper.setVisibility(View.GONE);
             mSettingsContainer.setKeyguardShowing(false);
         }
 
@@ -3201,131 +2953,6 @@
      * @return a ViewGroup that spans the entire panel which contains the quick settings
      */
     public ViewGroup getQuickSettingsOverlayParent() {
-        if (mHasQuickSettings) {
-            return mNotificationPanel;
-        } else {
-            return null;
-        }
-    }
-
-    public static boolean inBounds(View view, MotionEvent event, boolean orAbove) {
-        final int[] location = new int[2];
-        view.getLocationInWindow(location);
-        final int rx = (int) event.getRawX();
-        final int ry = (int) event.getRawY();
-        return rx >= location[0] && rx <= location[0] + view.getMeasuredWidth()
-                && (orAbove || ry >= location[1]) && ry <= location[1] + view.getMeasuredHeight();
-    }
-
-    private final class FlipperButton {
-        private final View mHolder;
-
-        private ImageView mSettingsButton, mNotificationButton;
-        private Animator mSettingsButtonAnim, mNotificationButtonAnim;
-
-        public FlipperButton(View holder) {
-            mHolder = holder;
-            mSettingsButton = (ImageView) holder.findViewById(R.id.settings_button);
-            if (mSettingsButton != null) {
-                mSettingsButton.setOnClickListener(mSettingsButtonListener);
-                if (mHasQuickSettings) {
-                    // the settings panel is hiding behind this button
-                    mSettingsButton.setImageResource(R.drawable.ic_notify_quicksettings);
-                    mSettingsButton.setVisibility(View.VISIBLE);
-                } else {
-                    // no settings panel, go straight to settings
-                    mSettingsButton.setVisibility(View.VISIBLE);
-                    mSettingsButton.setImageResource(R.drawable.ic_notify_settings);
-                }
-            }
-            mNotificationButton = (ImageView) holder.findViewById(R.id.notification_button);
-            if (mNotificationButton != null) {
-                mNotificationButton.setOnClickListener(mNotificationButtonListener);
-            }
-        }
-
-        public boolean inHolderBounds(MotionEvent event) {
-            return inBounds(mHolder, event, false);
-        }
-
-        public void provisionCheck(boolean provisioned) {
-            if (mSettingsButton != null) {
-                mSettingsButton.setEnabled(provisioned);
-            }
-        }
-
-        public void userSetup(boolean userSetup) {
-            if (mSettingsButton != null) {
-                mSettingsButton.setVisibility(userSetup ? View.VISIBLE : View.INVISIBLE);
-            }
-        }
-
-        public void reset() {
-            cancel();
-            mSettingsButton.setVisibility(View.VISIBLE);
-            mNotificationButton.setVisibility(View.GONE);
-        }
-
-        public void refreshLayout() {
-            if (mSettingsButton != null) {
-                // Force asset reloading
-                mSettingsButton.setImageDrawable(null);
-                mSettingsButton.setImageResource(R.drawable.ic_notify_quicksettings);
-            }
-
-            if (mNotificationButton != null) {
-                // Force asset reloading
-                mNotificationButton.setImageDrawable(null);
-                mNotificationButton.setImageResource(R.drawable.ic_notifications);
-            }
-        }
-
-        public void flipToSettings(boolean animate) {
-            mNotificationButton.setVisibility(View.VISIBLE);
-            if (animate) {
-                mSettingsButtonAnim = start(
-                    setVisibilityWhenDone(
-                        ObjectAnimator.ofFloat(mSettingsButton, View.ALPHA, 0f)
-                            .setDuration(FLIP_DURATION_OUT),
-                        mStackScroller, View.INVISIBLE));
-                mNotificationButtonAnim = start(
-                    startDelay(FLIP_DURATION_OUT,
-                        ObjectAnimator.ofFloat(mNotificationButton, View.ALPHA, 1f)
-                            .setDuration(FLIP_DURATION_IN)));
-            } else {
-                mSettingsButton.setAlpha(0f);
-                mSettingsButton.setVisibility(View.INVISIBLE);
-                mNotificationButton.setAlpha(1f);
-            }
-        }
-
-        public void flipToNotifications(boolean animate) {
-            mSettingsButton.setVisibility(View.VISIBLE);
-            if (animate) {
-                mNotificationButtonAnim = start(
-                    setVisibilityWhenDone(
-                        ObjectAnimator.ofFloat(mNotificationButton, View.ALPHA, 0f)
-                            .setDuration(FLIP_DURATION_OUT),
-                        mNotificationButton, View.INVISIBLE));
-
-                mSettingsButtonAnim = start(
-                    startDelay(FLIP_DURATION_OUT,
-                        ObjectAnimator.ofFloat(mSettingsButton, View.ALPHA, 1f)
-                            .setDuration(FLIP_DURATION_IN)));
-            } else {
-                mNotificationButton.setVisibility(View.INVISIBLE);
-                mNotificationButton.setAlpha(0f);
-                mSettingsButton.setAlpha(1f);
-            }
-        }
-
-        public void cancel() {
-            cancelAnim(mSettingsButtonAnim);
-            cancelAnim(mNotificationButtonAnim);
-        }
-
-        public void setVisibility(int vis) {
-            mHolder.setVisibility(vis);
-        }
+        return mNotificationPanel;
     }
 }
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 10c1625..e6de057 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
@@ -220,8 +220,6 @@
             panel.setAlpha(alpha);
         }
 
-        mBar.animateHeadsUp(mNotificationPanel == panel, mPanelExpandedFractionSum);
-
         mBar.updateCarrierLabelVisibility(false);
         mBar.userActivity();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
new file mode 100644
index 0000000..9d33930
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
@@ -0,0 +1,97 @@
+/*
+ * 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.util.AttributeSet;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.LinearLayout;
+import android.widget.RelativeLayout;
+
+import com.android.systemui.R;
+
+/**
+ * The view to manage the header area in the expanded status bar.
+ */
+public class StatusBarHeaderView extends RelativeLayout {
+
+    private boolean mExpanded;
+    private View mBackground;
+    private View mFlipper;
+
+    private int mCollapsedHeight;
+    private int mExpandedHeight;
+
+    public StatusBarHeaderView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        mBackground = findViewById(R.id.background);
+        mFlipper = findViewById(R.id.header_flipper);
+        loadDimens();
+    }
+
+    private void loadDimens() {
+        mCollapsedHeight = getResources().getDimensionPixelSize(
+                R.dimen.status_bar_header_height);
+        mExpandedHeight = getResources().getDimensionPixelSize(
+                R.dimen.status_bar_header_height_expanded);
+    }
+
+    public int getCollapsedHeight() {
+        return mCollapsedHeight;
+    }
+
+    public int getExpandedHeight() {
+        return mExpandedHeight;
+    }
+
+    public void setExpanded(boolean expanded) {
+        if (expanded != mExpanded) {
+            ViewGroup.LayoutParams lp = getLayoutParams();
+            lp.height = expanded ? mExpandedHeight : mCollapsedHeight;
+            setLayoutParams(lp);
+            mExpanded = expanded;
+        }
+    }
+
+    public void setExpansionEnabled(boolean enabled) {
+        mFlipper.setVisibility(enabled ? View.VISIBLE : View.GONE);
+    }
+
+    public void setExpansion(float height) {
+        if (height < mCollapsedHeight) {
+            height = mCollapsedHeight;
+        }
+        if (height > mExpandedHeight) {
+            height = mExpandedHeight;
+        }
+        if (mExpanded) {
+            mBackground.setTranslationY(-(mExpandedHeight - height));
+        } else {
+            mBackground.setTranslationY(0);
+        }
+    }
+
+    public View getBackgroundView() {
+        return mBackground;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
index 4c9264d..e802d185 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
@@ -56,11 +56,14 @@
     @Override
     protected boolean fitSystemWindows(Rect insets) {
         if (getFitsSystemWindows()) {
-            setPadding(insets.left, insets.top, insets.right, insets.bottom);
+            setPadding(insets.left, insets.top, insets.right, 0);
+            insets.left = 0;
+            insets.top = 0;
+            insets.right = 0;
         } else {
             setPadding(0, 0, 0, 0);
         }
-        return true;
+        return false;
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpNotificationView.java
index 72e22e9..81e2cb3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpNotificationView.java
@@ -237,6 +237,10 @@
     }
 
     @Override
+    public void onChildSnappedBack(View animView) {
+    }
+
+    @Override
     public View getChildAtPosition(MotionEvent ev) {
         return mContentHolder;
     }
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 e4e5fb1..59d717c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -97,6 +97,8 @@
     private StackScrollState mCurrentStackScrollState = new StackScrollState(this);
     private ArrayList<View> mChildrenToAddAnimated = new ArrayList<View>();
     private ArrayList<View> mChildrenToRemoveAnimated = new ArrayList<View>();
+    private ArrayList<View> mSnappedBackChildren = new ArrayList<View>();
+    private ArrayList<View> mDragAnimPendingChildren = new ArrayList<View>();
     private ArrayList<AnimationEvent> mAnimationEvents
             = new ArrayList<AnimationEvent>();
     private ArrayList<View> mSwipedOutViews = new ArrayList<View>();
@@ -377,11 +379,34 @@
             veto.performClick();
         }
         setSwipingInProgress(false);
+        if (mDragAnimPendingChildren.contains(v)) {
+            // We start the swipe and finish it in the same frame, we don't want any animation
+            // for the drag
+            mDragAnimPendingChildren.remove(v);
+        }
         mSwipedOutViews.add(v);
+        mStackScrollAlgorithm.onDragFinished(v);
+    }
+
+    @Override
+    public void onChildSnappedBack(View animView) {
+        mStackScrollAlgorithm.onDragFinished(animView);
+        if (!mDragAnimPendingChildren.contains(animView)) {
+            mSnappedBackChildren.add(animView);
+            requestChildrenUpdate();
+            mNeedsAnimation = true;
+        } else {
+            // We start the swipe and snap back in the same frame, we don't want any animation
+            mDragAnimPendingChildren.remove(animView);
+        }
     }
 
     public void onBeginDrag(View v) {
         setSwipingInProgress(true);
+        mDragAnimPendingChildren.add(v);
+        mStackScrollAlgorithm.onBeginDrag(v);
+        requestChildrenUpdate();
+        mNeedsAnimation = true;
     }
 
     public void onDragCancelled(View v) {
@@ -467,6 +492,9 @@
 
     @Override
     public boolean onTouchEvent(MotionEvent ev) {
+        if (!isEnabled()) {
+            return false;
+        }
         boolean scrollerWantsIt = false;
         if (!mSwipingInProgress) {
             scrollerWantsIt = onScrollTouch(ev);
@@ -934,12 +962,30 @@
     private void generateChildHierarchyEvents() {
         generateChildAdditionEvents();
         generateChildRemovalEvents();
+        generateSnapBackEvents();
+        generateDragEvents();
         generateTopPaddingEvent();
         mNeedsAnimation = false;
     }
 
+    private void generateSnapBackEvents() {
+        for (View child : mSnappedBackChildren) {
+            mAnimationEvents.add(new AnimationEvent(child,
+                    AnimationEvent.ANIMATION_TYPE_SNAP_BACK));
+        }
+        mSnappedBackChildren.clear();
+    }
+
+    private void generateDragEvents() {
+        for (View child : mDragAnimPendingChildren) {
+            mAnimationEvents.add(new AnimationEvent(child,
+                    AnimationEvent.ANIMATION_TYPE_START_DRAG));
+        }
+        mDragAnimPendingChildren.clear();
+    }
+
     private void generateChildRemovalEvents() {
-        for (View  child : mChildrenToRemoveAnimated) {
+        for (View child : mChildrenToRemoveAnimated) {
             boolean childWasSwipedOut = mSwipedOutViews.contains(child);
             int animationType = childWasSwipedOut
                     ? AnimationEvent.ANIMATION_TYPE_REMOVE_SWIPED_OUT
@@ -951,7 +997,7 @@
     }
 
     private void generateChildAdditionEvents() {
-        for (View  child : mChildrenToAddAnimated) {
+        for (View child : mChildrenToAddAnimated) {
             mAnimationEvents.add(new AnimationEvent(child,
                     AnimationEvent.ANIMATION_TYPE_ADD));
         }
@@ -1173,6 +1219,8 @@
         static int ANIMATION_TYPE_REMOVE = 2;
         static int ANIMATION_TYPE_REMOVE_SWIPED_OUT = 3;
         static int ANIMATION_TYPE_TOP_PADDING_CHANGED = 4;
+        static int ANIMATION_TYPE_START_DRAG = 5;
+        static int ANIMATION_TYPE_SNAP_BACK = 6;
 
         final long eventStartTime;
         final View changingView;
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 09d8d50..f7818c0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
@@ -61,6 +61,7 @@
     private ExpandableView mFirstChildWhileExpanding;
     private boolean mExpandedOnStart;
     private int mTopStackTotalSize;
+    private ArrayList<View> mDraggedViews = new ArrayList<View>();
 
     public StackScrollAlgorithm(Context context) {
         initConstants(context);
@@ -118,6 +119,34 @@
 
         // Phase 3:
         updateZValuesForState(resultState, algorithmState);
+
+        handleDraggedViews(resultState, algorithmState);
+    }
+
+    /**
+     * Handle the special state when views are being dragged
+     */
+    private void handleDraggedViews(StackScrollState resultState,
+            StackScrollAlgorithmState algorithmState) {
+        for (View draggedView : mDraggedViews) {
+            int childIndex = algorithmState.visibleChildren.indexOf(draggedView);
+            if (childIndex >= 0 && childIndex < algorithmState.visibleChildren.size() - 1) {
+                View nextChild = algorithmState.visibleChildren.get(childIndex + 1);
+                if (!mDraggedViews.contains(nextChild)) {
+                    // only if the view is not dragged itself we modify its state to be fully
+                    // visible
+                    StackScrollState.ViewState viewState = resultState.getViewStateForView(
+                            nextChild);
+                    // The child below the dragged one must be fully visible
+                    viewState.alpha = 1;
+                }
+
+                // Lets set the alpha to the one it currently has, as its currently being dragged
+                StackScrollState.ViewState viewState = resultState.getViewStateForView(draggedView);
+                // The dragged child should keep the set alpha
+                viewState.alpha = draggedView.getAlpha();
+            }
+        }
     }
 
     /**
@@ -566,6 +595,14 @@
         }
     }
 
+    public void onBeginDrag(View view) {
+        mDraggedViews.add(view);
+    }
+
+    public void onDragFinished(View view) {
+        mDraggedViews.remove(view);
+    }
+
     class StackScrollAlgorithmState {
 
         /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java
index 26cef36..70126f5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java
@@ -93,6 +93,7 @@
         int numChildren = mHostView.getChildCount();
         float previousNotificationEnd = 0;
         float previousNotificationStart = 0;
+        boolean previousNotificationIsSwiped = false;
         for (int i = 0; i < numChildren; i++) {
             ExpandableView child = (ExpandableView) mHostView.getChildAt(i);
             ViewState state = mStateMap.get(child);
@@ -153,12 +154,20 @@
 
                 // apply clipping and shadow
                 float newNotificationEnd = newYTranslation + newHeight;
+
+                // When the previous notification is swiped, we don't clip the content to the
+                // bottom of it.
+                float clipHeight = previousNotificationIsSwiped
+                        ? newHeight
+                        : newNotificationEnd - (previousNotificationEnd);
+
                 updateChildClippingAndBackground(child, newHeight,
-                        newNotificationEnd - (previousNotificationEnd),
+                        clipHeight,
                         (int) (newHeight - (previousNotificationStart - newYTranslation)));
 
-                previousNotificationStart = newYTranslation;
+                previousNotificationStart = newYTranslation + child.getClipTopAmount();
                 previousNotificationEnd = newNotificationEnd;
+                previousNotificationIsSwiped = child.getTranslationX() != 0;
             }
         }
     }
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 4dce288..2e700aa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
@@ -16,13 +16,21 @@
 
 package com.android.systemui.statusbar.stack;
 
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
 import android.animation.ValueAnimator;
 import android.view.View;
 import android.view.animation.AnimationUtils;
 import android.view.animation.Interpolator;
+
+import com.android.systemui.R;
 import com.android.systemui.statusbar.ExpandableView;
 
 import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.Stack;
 
 /**
  * An stack state animator which handles animations to new StackScrollStates
@@ -30,130 +38,372 @@
 public class StackStateAnimator {
 
     private static final int ANIMATION_DURATION = 360;
+    private static final int TAG_ANIMATOR_TRANSLATION_Y = R.id.translation_y_animator_tag;
+    private static final int TAG_ANIMATOR_TRANSLATION_Z = R.id.translation_z_animator_tag;
+    private static final int TAG_ANIMATOR_ALPHA = R.id.alpha_animator_tag;
+    private static final int TAG_ANIMATOR_HEIGHT = R.id.height_animator_tag;
+    private static final int TAG_ANIMATOR_TOP_INSET = R.id.top_inset_animator_tag;
+    private static final int TAG_END_TRANSLATION_Y = R.id.translation_y_animator_end_value_tag;
+    private static final int TAG_END_TRANSLATION_Z = R.id.translation_z_animator_end_value_tag;
+    private static final int TAG_END_ALPHA = R.id.alpha_animator_end_value_tag;
+    private static final int TAG_END_HEIGHT = R.id.height_animator_end_value_tag;
+    private static final int TAG_END_TOP_INSET = R.id.top_inset_animator_end_value_tag;
 
     private final Interpolator mFastOutSlowInInterpolator;
     public NotificationStackScrollLayout mHostLayout;
-    private boolean mAnimationIsRunning;
     private ArrayList<NotificationStackScrollLayout.AnimationEvent> mHandledEvents =
             new ArrayList<>();
+    private ArrayList<NotificationStackScrollLayout.AnimationEvent> mNewEvents =
+            new ArrayList<>();
+    private Set<Animator> mAnimatorSet = new HashSet<Animator>();
+    private Stack<AnimatorListenerAdapter> mAnimationListenerPool
+            = new Stack<AnimatorListenerAdapter>();
 
     public StackStateAnimator(NotificationStackScrollLayout hostLayout) {
         mHostLayout = hostLayout;
         mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(hostLayout.getContext(),
-                        android.R.interpolator.fast_out_slow_in);
+                android.R.interpolator.fast_out_slow_in);
     }
 
     public boolean isRunning() {
-        return mAnimationIsRunning;
+        return !mAnimatorSet.isEmpty();
     }
 
     public void startAnimationForEvents(
             ArrayList<NotificationStackScrollLayout.AnimationEvent> mAnimationEvents,
             StackScrollState finalState) {
-        int numEvents = mAnimationEvents.size();
-        if (numEvents == 0) {
-            // No events, so we don't perform any animation
-            return;
-        }
-        long lastEventStartTime = mAnimationEvents.get(numEvents - 1).eventStartTime;
-        long eventEnd = lastEventStartTime + ANIMATION_DURATION;
-        long currentTime = AnimationUtils.currentAnimationTimeMillis();
-        long newDuration = eventEnd - currentTime;
-        if (newDuration <= 0) {
-            // last event is long before this, so we don't do anything
-            return;
-        }
-        initializeAddedViewStates(mAnimationEvents, finalState);
+
+        processAnimationEvents(mAnimationEvents, finalState);
+
+        boolean hasNewEvents = !mNewEvents.isEmpty();
         int childCount = mHostLayout.getChildCount();
-        boolean isFirstAnimatingView = true;
         for (int i = 0; i < childCount; i++) {
             final ExpandableView child = (ExpandableView) mHostLayout.getChildAt(i);
             StackScrollState.ViewState viewState = finalState.getViewStateForView(child);
             if (viewState == null) {
                 continue;
             }
-            int childVisibility = child.getVisibility();
-            boolean wasVisible = childVisibility == View.VISIBLE;
-            final float alpha = viewState.alpha;
-            if (!wasVisible && alpha != 0 && !viewState.gone) {
-                child.setVisibility(View.VISIBLE);
-            }
 
-            startPropertyAnimation(newDuration, isFirstAnimatingView, child, viewState, alpha);
+            startAnimations(child, viewState, hasNewEvents);
 
-            // TODO: animate clipBounds
             child.setClipBounds(null);
-            int currentHeigth = child.getActualHeight();
-            if (viewState.height != currentHeigth) {
-                startHeightAnimation(newDuration, child, viewState, currentHeigth);
-            }
-            isFirstAnimatingView = false;
         }
-        mAnimationIsRunning = true;
+        if (!isRunning()) {
+            // no child has preformed any animation, lets finish
+            onAnimationFinished();
+        }
     }
 
-    private void startPropertyAnimation(long newDuration, final boolean hasFinishAction,
-            final ExpandableView child, StackScrollState.ViewState viewState, final float alpha) {
-        child.animate().setInterpolator(mFastOutSlowInInterpolator)
-                .translationY(viewState.yTranslation)
-                .translationZ(viewState.zTranslation)
-                .setDuration(newDuration)
-                .withEndAction(new Runnable() {
-                    @Override
-                    public void run() {
-                        mAnimationIsRunning = false;
-                        if (hasFinishAction) {
-                            mHandledEvents.clear();
-                            mHostLayout.onChildAnimationFinished();
-                        }
-                        if (alpha == 0) {
-                            child.setVisibility(View.INVISIBLE);
-                        }
-                    }
-                });
+    /**
+     * Start an animation to the given viewState
+     */
+    private void startAnimations(final ExpandableView child, StackScrollState.ViewState viewState,
+            boolean hasNewEvents) {
+        int childVisibility = child.getVisibility();
+        boolean wasVisible = childVisibility == View.VISIBLE;
+        final float alpha = viewState.alpha;
+        if (!wasVisible && alpha != 0 && !viewState.gone) {
+            child.setVisibility(View.VISIBLE);
+        }
+        // start translationY animation
+        if (child.getTranslationY() != viewState.yTranslation) {
+            startYTranslationAnimation(child, viewState, hasNewEvents);
+        }
+        // start translationZ animation
+        if (child.getTranslationZ() != viewState.zTranslation) {
+            startZTranslationAnimation(child, viewState, hasNewEvents);
+        }
+        // start alpha animation
         if (alpha != child.getAlpha()) {
-            child.animate().withLayer().alpha(alpha);
+            startAlphaAnimation(child, viewState, hasNewEvents);
+        }
+        // start height animation
+        if (viewState.height != child.getActualHeight()) {
+            startHeightAnimation(child, viewState, hasNewEvents);
         }
     }
 
-    private void startHeightAnimation(long newDuration, final ExpandableView child,
-            StackScrollState.ViewState viewState, int currentHeigth) {
-        ValueAnimator heightAnimator = ValueAnimator.ofInt(currentHeigth, viewState.height);
-        heightAnimator.setInterpolator(mFastOutSlowInInterpolator);
-        heightAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+    private void startHeightAnimation(final ExpandableView child,
+            StackScrollState.ViewState viewState, boolean hasNewEvents) {
+        Integer previousEndValue = getChildTag(child,TAG_END_HEIGHT);
+        if (previousEndValue != null && previousEndValue == viewState.height) {
+            return;
+        }
+        ValueAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_HEIGHT);
+        long newDuration = cancelAnimatorAndGetNewDuration(previousAnimator, hasNewEvents);
+        if (newDuration <= 0) {
+            // no new animation needed, let's just apply the value
+            child.setActualHeight(viewState.height);
+            if (previousAnimator != null && !isRunning()) {
+                onAnimationFinished();
+            }
+            return;
+        }
+
+        ValueAnimator animator = ValueAnimator.ofInt(child.getActualHeight(), viewState.height);
+        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
             @Override
             public void onAnimationUpdate(ValueAnimator animation) {
                 child.setActualHeight((int) animation.getAnimatedValue());
             }
         });
-        heightAnimator.setDuration(newDuration);
-        heightAnimator.start();
+        animator.setInterpolator(mFastOutSlowInInterpolator);
+        animator.setDuration(newDuration);
+        animator.addListener(getGlobalAnimationFinishedListener());
+        // remove the tag when the animation is finished
+        animator.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                child.setTag(TAG_ANIMATOR_HEIGHT, null);
+                child.setTag(TAG_END_HEIGHT, null);
+            }
+        });
+        startInstantly(animator);
+        child.setTag(TAG_ANIMATOR_HEIGHT, animator);
+        child.setTag(TAG_END_HEIGHT, viewState.height);
+    }
+
+    private void startAlphaAnimation(final ExpandableView child,
+            final StackScrollState.ViewState viewState, boolean hasNewEvents) {
+        final float endAlpha = viewState.alpha;
+        Float previousEndValue = getChildTag(child,TAG_END_ALPHA);
+        if (previousEndValue != null && previousEndValue == endAlpha) {
+            return;
+        }
+        ObjectAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_ALPHA);
+        long newDuration = cancelAnimatorAndGetNewDuration(previousAnimator, hasNewEvents);
+        if (newDuration <= 0) {
+            // no new animation needed, let's just apply the value
+            child.setAlpha(endAlpha);
+            if (endAlpha == 0) {
+                child.setVisibility(View.INVISIBLE);
+            }
+            if (previousAnimator != null && !isRunning()) {
+                onAnimationFinished();
+            }
+            return;
+        }
+
+        ObjectAnimator animator = ObjectAnimator.ofFloat(child, View.ALPHA,
+                child.getAlpha(), endAlpha);
+        animator.setInterpolator(mFastOutSlowInInterpolator);
+        // Handle layer type
+        final int currentLayerType = child.getLayerType();
+        child.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+        animator.addListener(new AnimatorListenerAdapter() {
+            public boolean mWasCancelled;
+
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                child.setLayerType(currentLayerType, null);
+                if (endAlpha == 0 && !mWasCancelled) {
+                    child.setVisibility(View.INVISIBLE);
+                }
+                child.setTag(TAG_ANIMATOR_ALPHA, null);
+                child.setTag(TAG_END_ALPHA, null);
+            }
+
+            @Override
+            public void onAnimationCancel(Animator animation) {
+                mWasCancelled = true;
+            }
+
+            @Override
+            public void onAnimationStart(Animator animation) {
+                mWasCancelled = false;
+            }
+        });
+        animator.setDuration(newDuration);
+        animator.addListener(getGlobalAnimationFinishedListener());
+        // remove the tag when the animation is finished
+        animator.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+
+            }
+        });
+        startInstantly(animator);
+        child.setTag(TAG_ANIMATOR_ALPHA, animator);
+        child.setTag(TAG_END_ALPHA, endAlpha);
+    }
+
+    private void startZTranslationAnimation(final ExpandableView child,
+            final StackScrollState.ViewState viewState, boolean hasNewEvents) {
+        Float previousEndValue = getChildTag(child,TAG_END_TRANSLATION_Z);
+        if (previousEndValue != null && previousEndValue == viewState.zTranslation) {
+            return;
+        }
+        ObjectAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_TRANSLATION_Z);
+        long newDuration = cancelAnimatorAndGetNewDuration(previousAnimator, hasNewEvents);
+        if (newDuration <= 0) {
+            // no new animation needed, let's just apply the value
+            child.setTranslationZ(viewState.zTranslation);
+
+            if (previousAnimator != null && !isRunning()) {
+                onAnimationFinished();
+            }
+            return;
+        }
+
+        ObjectAnimator animator = ObjectAnimator.ofFloat(child, View.TRANSLATION_Z,
+                child.getTranslationZ(), viewState.zTranslation);
+        animator.setInterpolator(mFastOutSlowInInterpolator);
+        animator.setDuration(newDuration);
+        animator.addListener(getGlobalAnimationFinishedListener());
+        // remove the tag when the animation is finished
+        animator.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                child.setTag(TAG_ANIMATOR_TRANSLATION_Z, null);
+                child.setTag(TAG_END_TRANSLATION_Z, null);
+            }
+        });
+        startInstantly(animator);
+        child.setTag(TAG_ANIMATOR_TRANSLATION_Z, animator);
+        child.setTag(TAG_END_TRANSLATION_Z, viewState.zTranslation);
+    }
+
+    private void startYTranslationAnimation(final ExpandableView child,
+            StackScrollState.ViewState viewState, boolean hasNewEvents) {
+        Float previousEndValue = getChildTag(child,TAG_END_TRANSLATION_Y);
+        if (previousEndValue != null && previousEndValue == viewState.yTranslation) {
+            return;
+        }
+        ObjectAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_TRANSLATION_Y);
+        long newDuration = cancelAnimatorAndGetNewDuration(previousAnimator, hasNewEvents);
+        if (newDuration <= 0) {
+            // no new animation needed, let's just apply the value
+            child.setTranslationY(viewState.yTranslation);
+            if (previousAnimator != null && !isRunning()) {
+                onAnimationFinished();
+            }
+            return;
+        }
+
+        ObjectAnimator animator = ObjectAnimator.ofFloat(child, View.TRANSLATION_Y,
+                child.getTranslationY(), viewState.yTranslation);
+        animator.setInterpolator(mFastOutSlowInInterpolator);
+        animator.setDuration(newDuration);
+        animator.addListener(getGlobalAnimationFinishedListener());
+        // remove the tag when the animation is finished
+        animator.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                child.setTag(TAG_ANIMATOR_TRANSLATION_Y, null);
+                child.setTag(TAG_END_TRANSLATION_Y, null);
+            }
+        });
+        startInstantly(animator);
+        child.setTag(TAG_ANIMATOR_TRANSLATION_Y, animator);
+        child.setTag(TAG_END_TRANSLATION_Y, viewState.yTranslation);
     }
 
     /**
-     * Initialize the viewStates for the added children
+     * Start an animator instantly instead of waiting on the next synchronization frame
+     */
+    private void startInstantly(ValueAnimator animator) {
+        animator.start();
+        animator.setCurrentPlayTime(0);
+    }
+
+    /**
+     * @return an adapter which ensures that onAnimationFinished is called once no animation is
+     *         running anymore
+     */
+    private AnimatorListenerAdapter getGlobalAnimationFinishedListener() {
+        if (!mAnimationListenerPool.empty()) {
+            return mAnimationListenerPool.pop();
+        }
+
+        // We need to create a new one, no reusable ones found
+        return new AnimatorListenerAdapter() {
+            private boolean mWasCancelled;
+
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                mAnimatorSet.remove(animation);
+                if (mAnimatorSet.isEmpty() && !mWasCancelled) {
+                    onAnimationFinished();
+                }
+                mAnimationListenerPool.push(this);
+            }
+
+            @Override
+            public void onAnimationCancel(Animator animation) {
+                mWasCancelled = true;
+            }
+
+            @Override
+            public void onAnimationStart(Animator animation) {
+                mAnimatorSet.add(animation);
+                mWasCancelled = false;
+            }
+        };
+    }
+
+    private <T> T getChildTag(View child, int tag) {
+        return (T) child.getTag(tag);
+    }
+
+    /**
+     * Cancel the previous animator and get the duration of the new animation.
      *
-     * @param animationEvents the animation events who contain the added children
+     * @param previousAnimator the animator which was running before
+     * @param hasNewEvents indicating whether new events came in in this animation
+     * @return the new duration
+     */
+    private long cancelAnimatorAndGetNewDuration(ValueAnimator previousAnimator,
+            boolean hasNewEvents) {
+        long newDuration = ANIMATION_DURATION;
+        if (previousAnimator != null) {
+            if (!hasNewEvents) {
+                // This is only an update, no new event came in. lets just take the remaining
+                // duration as the new duration
+                newDuration = previousAnimator.getDuration()
+                        - previousAnimator.getCurrentPlayTime();
+            }
+            previousAnimator.cancel();
+        } else if (!hasNewEvents){
+            newDuration = 0;
+        }
+        return newDuration;
+    }
+
+    private void onAnimationFinished() {
+        mHandledEvents.clear();
+        mNewEvents.clear();
+        mHostLayout.onChildAnimationFinished();
+    }
+
+    /**
+     * Process the animationEvents for a new animation
+     *
+     * @param animationEvents the animation events for the animation to perform
      * @param finalState the final state to animate to
      */
-    private void initializeAddedViewStates(
+    private void processAnimationEvents(
             ArrayList<NotificationStackScrollLayout.AnimationEvent> animationEvents,
             StackScrollState finalState) {
+        mNewEvents.clear();
         for (NotificationStackScrollLayout.AnimationEvent event: animationEvents) {
             View changingView = event.changingView;
-            if (event.animationType == NotificationStackScrollLayout.AnimationEvent
-                    .ANIMATION_TYPE_ADD && !mHandledEvents.contains(event)) {
+            if (!mHandledEvents.contains(event)) {
+                if (event.animationType == NotificationStackScrollLayout.AnimationEvent
+                        .ANIMATION_TYPE_ADD) {
 
-                // This item is added, initialize it's properties.
-                StackScrollState.ViewState viewState = finalState.getViewStateForView(changingView);
-                if (viewState == null) {
-                    // The position for this child was never generated, let's continue.
-                    continue;
+                    // This item is added, initialize it's properties.
+                    StackScrollState.ViewState viewState = finalState
+                            .getViewStateForView(changingView);
+                    if (viewState == null) {
+                        // The position for this child was never generated, let's continue.
+                        continue;
+                    }
+                    changingView.setAlpha(0);
+                    changingView.setTranslationY(viewState.yTranslation);
+                    changingView.setTranslationZ(viewState.zTranslation);
                 }
-                changingView.setAlpha(0);
-                changingView.setTranslationY(viewState.yTranslation);
-                changingView.setTranslationZ(viewState.zTranslation);
                 mHandledEvents.add(event);
+                mNewEvents.add(event);
             }
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
index d615542..d6a8885 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
@@ -78,7 +78,8 @@
     }
 
     @Override
-    public void setImeWindowStatus(IBinder token, int vis, int backDisposition) {
+    public void setImeWindowStatus(IBinder token, int vis, int backDisposition,
+            boolean showImeSwitcher) {
     }
 
     @Override
@@ -120,11 +121,6 @@
     }
 
     @Override
-    protected int getExpandedViewMaxHeight() {
-        return 0;
-    }
-
-    @Override
     protected boolean shouldDisableNavbarGestures() {
         return true;
     }
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 45cdb65..ea03eb7 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -74,7 +74,7 @@
 import android.net.NetworkUtils;
 import android.net.Proxy;
 import android.net.ProxyDataTracker;
-import android.net.ProxyProperties;
+import android.net.ProxyInfo;
 import android.net.RouteInfo;
 import android.net.SamplingDataTracker;
 import android.net.Uri;
@@ -406,12 +406,12 @@
     private ArrayList mInetLog;
 
     // track the current default http proxy - tell the world if we get a new one (real change)
-    private ProxyProperties mDefaultProxy = null;
+    private ProxyInfo mDefaultProxy = null;
     private Object mProxyLock = new Object();
     private boolean mDefaultProxyDisabled = false;
 
     // track the global proxy.
-    private ProxyProperties mGlobalProxy = null;
+    private ProxyInfo mGlobalProxy = null;
 
     private PacManager mPacManager = null;
 
@@ -3192,7 +3192,7 @@
                     break;
                 }
                 case EVENT_PROXY_HAS_CHANGED: {
-                    handleApplyDefaultProxy((ProxyProperties)msg.obj);
+                    handleApplyDefaultProxy((ProxyInfo)msg.obj);
                     break;
                 }
             }
@@ -3410,19 +3410,19 @@
         return;
     }
 
-    public ProxyProperties getProxy() {
+    public ProxyInfo getProxy() {
         // this information is already available as a world read/writable jvm property
         // so this API change wouldn't have a benifit.  It also breaks the passing
         // of proxy info to all the JVMs.
         // enforceAccessPermission();
         synchronized (mProxyLock) {
-            ProxyProperties ret = mGlobalProxy;
+            ProxyInfo ret = mGlobalProxy;
             if ((ret == null) && !mDefaultProxyDisabled) ret = mDefaultProxy;
             return ret;
         }
     }
 
-    public void setGlobalProxy(ProxyProperties proxyProperties) {
+    public void setGlobalProxy(ProxyInfo proxyProperties) {
         enforceConnectivityInternalPermission();
 
         synchronized (mProxyLock) {
@@ -3435,18 +3435,18 @@
             String exclList = "";
             String pacFileUrl = "";
             if (proxyProperties != null && (!TextUtils.isEmpty(proxyProperties.getHost()) ||
-                    !TextUtils.isEmpty(proxyProperties.getPacFileUrl()))) {
+                    (proxyProperties.getPacFileUrl() != null))) {
                 if (!proxyProperties.isValid()) {
                     if (DBG)
                         log("Invalid proxy properties, ignoring: " + proxyProperties.toString());
                     return;
                 }
-                mGlobalProxy = new ProxyProperties(proxyProperties);
+                mGlobalProxy = new ProxyInfo(proxyProperties);
                 host = mGlobalProxy.getHost();
                 port = mGlobalProxy.getPort();
-                exclList = mGlobalProxy.getExclusionList();
+                exclList = mGlobalProxy.getExclusionListAsString();
                 if (proxyProperties.getPacFileUrl() != null) {
-                    pacFileUrl = proxyProperties.getPacFileUrl();
+                    pacFileUrl = proxyProperties.getPacFileUrl().toString();
                 }
             } else {
                 mGlobalProxy = null;
@@ -3478,11 +3478,11 @@
                 Settings.Global.GLOBAL_HTTP_PROXY_EXCLUSION_LIST);
         String pacFileUrl = Settings.Global.getString(res, Settings.Global.GLOBAL_HTTP_PROXY_PAC);
         if (!TextUtils.isEmpty(host) || !TextUtils.isEmpty(pacFileUrl)) {
-            ProxyProperties proxyProperties;
+            ProxyInfo proxyProperties;
             if (!TextUtils.isEmpty(pacFileUrl)) {
-                proxyProperties = new ProxyProperties(pacFileUrl);
+                proxyProperties = new ProxyInfo(pacFileUrl);
             } else {
-                proxyProperties = new ProxyProperties(host, port, exclList);
+                proxyProperties = new ProxyInfo(host, port, exclList);
             }
             if (!proxyProperties.isValid()) {
                 if (DBG) log("Invalid proxy properties, ignoring: " + proxyProperties.toString());
@@ -3495,7 +3495,7 @@
         }
     }
 
-    public ProxyProperties getGlobalProxy() {
+    public ProxyInfo getGlobalProxy() {
         // this information is already available as a world read/writable jvm property
         // so this API change wouldn't have a benifit.  It also breaks the passing
         // of proxy info to all the JVMs.
@@ -3505,9 +3505,9 @@
         }
     }
 
-    private void handleApplyDefaultProxy(ProxyProperties proxy) {
+    private void handleApplyDefaultProxy(ProxyInfo proxy) {
         if (proxy != null && TextUtils.isEmpty(proxy.getHost())
-                && TextUtils.isEmpty(proxy.getPacFileUrl())) {
+                && (proxy.getPacFileUrl() == null)) {
             proxy = null;
         }
         synchronized (mProxyLock) {
@@ -3544,13 +3544,13 @@
                     return;
                 }
             }
-            ProxyProperties p = new ProxyProperties(data[0], proxyPort, "");
+            ProxyInfo p = new ProxyInfo(data[0], proxyPort, "");
             setGlobalProxy(p);
         }
     }
 
-    private void sendProxyBroadcast(ProxyProperties proxy) {
-        if (proxy == null) proxy = new ProxyProperties("", 0, "");
+    private void sendProxyBroadcast(ProxyInfo proxy) {
+        if (proxy == null) proxy = new ProxyInfo("", 0, "");
         if (mPacManager.setCurrentProxyScriptUrl(proxy)) return;
         if (DBG) log("sending Proxy Broadcast for " + proxy);
         Intent intent = new Intent(Proxy.PROXY_CHANGE_ACTION);
diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java
index 7ed1cc7..10315a7 100644
--- a/services/core/java/com/android/server/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/InputMethodManagerService.java
@@ -1532,14 +1532,17 @@
                 }
                 mImeWindowVis = vis;
                 mBackDisposition = backDisposition;
-                if (mStatusBar != null) {
-                    mStatusBar.setImeWindowStatus(token, vis, backDisposition);
-                }
                 final boolean iconVisibility = ((vis & (InputMethodService.IME_ACTIVE)) != 0)
                         && (mWindowManagerService.isHardKeyboardAvailable()
                                 || (vis & (InputMethodService.IME_VISIBLE)) != 0);
+                final boolean needsToShowImeSwitcher = iconVisibility
+                        && needsToShowImeSwitchOngoingNotification();
+                if (mStatusBar != null) {
+                    mStatusBar.setImeWindowStatus(token, vis, backDisposition,
+                            needsToShowImeSwitcher);
+                }
                 final InputMethodInfo imi = mMethodMap.get(mCurMethodId);
-                if (imi != null && iconVisibility && needsToShowImeSwitchOngoingNotification()) {
+                if (imi != null && needsToShowImeSwitcher) {
                     // Used to load label
                     final CharSequence title = mRes.getText(
                             com.android.internal.R.string.select_input_method);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index cbb8377..0a02e17 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -136,7 +136,7 @@
 import android.content.res.Configuration;
 import android.graphics.Bitmap;
 import android.net.Proxy;
-import android.net.ProxyProperties;
+import android.net.ProxyInfo;
 import android.net.Uri;
 import android.os.Binder;
 import android.os.Build;
@@ -1323,7 +1323,7 @@
                 }
             } break;
             case UPDATE_HTTP_PROXY_MSG: {
-                ProxyProperties proxy = (ProxyProperties)msg.obj;
+                ProxyInfo proxy = (ProxyInfo)msg.obj;
                 String host = "";
                 String port = "";
                 String exclList = "";
@@ -1331,8 +1331,8 @@
                 if (proxy != null) {
                     host = proxy.getHost();
                     port = Integer.toString(proxy.getPort());
-                    exclList = proxy.getExclusionList();
-                    pacFileUrl = proxy.getPacFileUrl();
+                    exclList = proxy.getExclusionListAsString();
+                    pacFileUrl = proxy.getPacFileUrl().toString();
                 }
                 synchronized (ActivityManagerService.this) {
                     for (int i = mLruProcesses.size() - 1 ; i >= 0 ; i--) {
@@ -13758,7 +13758,7 @@
         }
 
         if (Proxy.PROXY_CHANGE_ACTION.equals(intent.getAction())) {
-            ProxyProperties proxy = intent.getParcelableExtra("proxy");
+            ProxyInfo proxy = intent.getParcelableExtra("proxy");
             mHandler.sendMessage(mHandler.obtainMessage(UPDATE_HTTP_PROXY_MSG, proxy));
         }
 
diff --git a/services/core/java/com/android/server/connectivity/PacManager.java b/services/core/java/com/android/server/connectivity/PacManager.java
index 8815d0f..0749f24 100644
--- a/services/core/java/com/android/server/connectivity/PacManager.java
+++ b/services/core/java/com/android/server/connectivity/PacManager.java
@@ -24,7 +24,7 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.ServiceConnection;
-import android.net.ProxyProperties;
+import android.net.ProxyInfo;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.RemoteException;
@@ -157,14 +157,14 @@
      * @param proxy Proxy information that is about to be broadcast.
      * @return Returns true when the broadcast should not be sent
      */
-    public synchronized boolean setCurrentProxyScriptUrl(ProxyProperties proxy) {
-        if (!TextUtils.isEmpty(proxy.getPacFileUrl())) {
+    public synchronized boolean setCurrentProxyScriptUrl(ProxyInfo proxy) {
+        if (proxy.getPacFileUrl() != null) {
             if (proxy.getPacFileUrl().equals(mPacUrl) && (proxy.getPort() > 0)) {
                 // Allow to send broadcast, nothing to do.
                 return false;
             }
             synchronized (mProxyLock) {
-                mPacUrl = proxy.getPacFileUrl();
+                mPacUrl = proxy.getPacFileUrl().toString();
             }
             mCurrentDelay = DELAY_1;
             mHasSentBroadcast = false;
@@ -268,7 +268,7 @@
         // Already bound no need to bind again.
         if ((mProxyConnection != null) && (mConnection != null)) {
             if (mLastPort != -1) {
-                sendPacBroadcast(new ProxyProperties(mPacUrl, mLastPort));
+                sendPacBroadcast(new ProxyInfo(mPacUrl, mLastPort));
             } else {
                 Log.e(TAG, "Received invalid port from Local Proxy,"
                         + " PAC will not be operational");
@@ -362,7 +362,7 @@
         mLastPort = -1;
     }
 
-    private void sendPacBroadcast(ProxyProperties proxy) {
+    private void sendPacBroadcast(ProxyInfo proxy) {
         mConnectivityHandler.sendMessage(mConnectivityHandler.obtainMessage(mProxyMessage, proxy));
     }
 
@@ -371,7 +371,7 @@
             return;
         }
         if (!mHasSentBroadcast) {
-            sendPacBroadcast(new ProxyProperties(mPacUrl, mLastPort));
+            sendPacBroadcast(new ProxyInfo(mPacUrl, mLastPort));
             mHasSentBroadcast = true;
         }
     }
diff --git a/services/core/java/com/android/server/dreams/DreamManagerService.java b/services/core/java/com/android/server/dreams/DreamManagerService.java
index fd2f8a1..8968da3 100644
--- a/services/core/java/com/android/server/dreams/DreamManagerService.java
+++ b/services/core/java/com/android/server/dreams/DreamManagerService.java
@@ -642,8 +642,9 @@
             try {
                 synchronized (mMcuHal) {
                     if (mReleased) {
-                        throw new IllegalStateException("This operation cannot be performed "
-                                + "because the dream has ended.");
+                        Slog.w(TAG, "Ignoring message to MCU HAL because the dream "
+                                + "has already ended: " + msg);
+                        return null;
                     }
                     return mMcuHal.sendMessage(msg, arg);
                 }
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index 3dc17fc..015032b 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -17,6 +17,7 @@
 package com.android.server.media;
 
 import android.content.Intent;
+import android.content.pm.PackageManager;
 import android.media.routeprovider.RouteRequest;
 import android.media.session.ISessionController;
 import android.media.session.ISessionControllerCallback;
@@ -59,6 +60,24 @@
 public class MediaSessionRecord implements IBinder.DeathRecipient {
     private static final String TAG = "MediaSessionRecord";
 
+    /**
+     * These are the playback states that count as currently active.
+     */
+    private static final int[] ACTIVE_STATES = {
+            PlaybackState.PLAYSTATE_FAST_FORWARDING,
+            PlaybackState.PLAYSTATE_REWINDING,
+            PlaybackState.PLAYSTATE_SKIPPING_BACKWARDS,
+            PlaybackState.PLAYSTATE_SKIPPING_FORWARDS,
+            PlaybackState.PLAYSTATE_BUFFERING,
+            PlaybackState.PLAYSTATE_CONNECTING,
+            PlaybackState.PLAYSTATE_PLAYING };
+
+    /**
+     * The length of time a session will still be considered active after
+     * pausing in ms.
+     */
+    private static final int ACTIVE_BUFFER = 30000;
+
     private final MessageHandler mHandler;
 
     private final int mPid;
@@ -74,21 +93,22 @@
             new ArrayList<ISessionControllerCallback>();
     private final ArrayList<RouteRequest> mRequests = new ArrayList<RouteRequest>();
 
-    private boolean mTransportPerformerEnabled = false;
     private RouteInfo mRoute;
     private RouteOptions mRequest;
     private RouteConnectionRecord mConnection;
     // TODO define a RouteState class with relevant info
     private int mRouteState;
+    private long mFlags;
 
     // TransportPerformer fields
 
     private MediaMetadata mMetadata;
     private PlaybackState mPlaybackState;
     private int mRatingType;
+    private long mLastActiveTime;
     // End TransportPerformer fields
 
-    private boolean mIsPublished = false;
+    private boolean mIsActive = false;
 
     public MediaSessionRecord(int pid, String packageName, ISessionCallback cb, String tag,
             MediaSessionService service, Handler handler) {
@@ -148,6 +168,35 @@
     }
 
     /**
+     * Get this session's flags.
+     *
+     * @return The flags for this session.
+     */
+    public long getFlags() {
+        return mFlags;
+    }
+
+    /**
+     * Check if this session has the specified flag.
+     *
+     * @param flag The flag to check.
+     * @return True if this session has that flag set, false otherwise.
+     */
+    public boolean hasFlag(int flag) {
+        return (mFlags & flag) != 0;
+    }
+
+    /**
+     * Check if this session has system priorty and should receive media buttons
+     * before any other sessions.
+     *
+     * @return True if this is a system priority session, false otherwise
+     */
+    public boolean isSystemPriority() {
+        return (mFlags & Session.FLAG_EXCLUSIVE_GLOBAL_PRIORITY) != 0;
+    }
+
+    /**
      * Set the selected route. This does not connect to the route, just notifies
      * the app that a new route has been selected.
      *
@@ -215,12 +264,36 @@
     }
 
     /**
-     * Check if this session has been published by the app yet.
+     * Check if this session has been set to active by the app.
      *
-     * @return True if it has been published, false otherwise.
+     * @return True if the session is active, false otherwise.
      */
-    public boolean isPublished() {
-        return mIsPublished;
+    public boolean isActive() {
+        return mIsActive;
+    }
+
+    /**
+     * Check if the session is currently performing playback. This will also
+     * return true if the session was recently paused.
+     *
+     * @return True if the session is performing playback, false otherwise.
+     */
+    public boolean isPlaybackActive() {
+        int state = mPlaybackState == null ? 0 : mPlaybackState.getState();
+        if (isActiveState(state)) {
+            return true;
+        }
+        if (state == mPlaybackState.PLAYSTATE_PAUSED) {
+            long inactiveTime = SystemClock.uptimeMillis() - mLastActiveTime;
+            if (inactiveTime < ACTIVE_BUFFER) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public boolean isTransportControlEnabled() {
+        return hasFlag(Session.FLAG_HANDLES_TRANSPORT_CONTROLS);
     }
 
     @Override
@@ -234,11 +307,11 @@
         final String indent = prefix + "  ";
         pw.println(indent + "pid=" + mPid);
         pw.println(indent + "info=" + mSessionInfo.toString());
-        pw.println(indent + "published=" + mIsPublished);
-        pw.println(indent + "transport controls enabled=" + mTransportPerformerEnabled);
+        pw.println(indent + "published=" + mIsActive);
+        pw.println(indent + "flags=" + mFlags);
         pw.println(indent + "rating type=" + mRatingType);
         pw.println(indent + "controllers: " + mControllerCallbacks.size());
-        pw.println(indent + "state=" + mPlaybackState.toString());
+        pw.println(indent + "state=" + (mPlaybackState == null ? null : mPlaybackState.toString()));
         pw.println(indent + "metadata:" + getShortMetadataString());
         pw.println(indent + "route requests {");
         int size = mRequests.size();
@@ -251,6 +324,15 @@
         pw.println(indent + "params=" + (mRequest == null ? null : mRequest.toString()));
     }
 
+    private boolean isActiveState(int state) {
+        for (int i = 0; i < ACTIVE_STATES.length; i++) {
+            if (ACTIVE_STATES[i] == state) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     private String getShortMetadataString() {
         int fields = mMetadata == null ? 0 : mMetadata.size();
         String title = mMetadata == null ? null : mMetadata
@@ -393,12 +475,21 @@
         }
 
         @Override
-        public void publish() {
-            mIsPublished = true; // TODO push update to service
+        public void setActive(boolean active) {
+            mIsActive = active;
+            mService.updateSession(MediaSessionRecord.this);
+            mHandler.post(MessageHandler.MSG_UPDATE_SESSION_STATE);
         }
+
         @Override
-        public void setTransportPerformerEnabled() {
-            mTransportPerformerEnabled = true;
+        public void setFlags(int flags) {
+            if ((flags & Session.FLAG_EXCLUSIVE_GLOBAL_PRIORITY) != 0) {
+                int pid = getCallingPid();
+                int uid = getCallingUid();
+                mService.enforcePhoneStatePermission(pid, uid);
+            }
+            mFlags = flags;
+            mHandler.post(MessageHandler.MSG_UPDATE_SESSION_STATE);
         }
 
         @Override
@@ -409,7 +500,13 @@
 
         @Override
         public void setPlaybackState(PlaybackState state) {
+            int oldState = mPlaybackState == null ? 0 : mPlaybackState.getState();
+            int newState = state == null ? 0 : state.getState();
+            if (isActiveState(oldState) && newState == PlaybackState.PLAYSTATE_PAUSED) {
+                mLastActiveTime = SystemClock.elapsedRealtime();
+            }
             mPlaybackState = state;
+            mService.onSessionPlaystateChange(MediaSessionRecord.this, oldState, newState);
             mHandler.post(MessageHandler.MSG_UPDATE_PLAYBACK_STATE);
         }
 
@@ -673,7 +770,7 @@
 
         @Override
         public boolean isTransportControlEnabled() {
-            return mTransportPerformerEnabled;
+            return MediaSessionRecord.this.isTransportControlEnabled();
         }
 
         @Override
@@ -689,6 +786,7 @@
         private static final int MSG_SEND_EVENT = 4;
         private static final int MSG_UPDATE_ROUTE_FILTERS = 5;
         private static final int MSG_SEND_COMMAND = 6;
+        private static final int MSG_UPDATE_SESSION_STATE = 7;
 
         public MessageHandler(Looper looper) {
             super(looper);
@@ -713,6 +811,9 @@
                             (Pair<RouteCommand, ResultReceiver>) msg.obj;
                     pushRouteCommand(cmd.first, cmd.second);
                     break;
+                case MSG_UPDATE_SESSION_STATE:
+                    // TODO add session state
+                    break;
             }
         }
 
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index 107f6ad..fb858fc 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -17,17 +17,25 @@
 package com.android.server.media;
 
 import android.Manifest;
+import android.app.ActivityManager;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.media.routeprovider.RouteRequest;
 import android.media.session.ISession;
 import android.media.session.ISessionCallback;
+import android.media.session.ISessionController;
 import android.media.session.ISessionManager;
+import android.media.session.PlaybackState;
 import android.media.session.RouteInfo;
 import android.media.session.RouteOptions;
 import android.os.Binder;
 import android.os.Handler;
+import android.os.IBinder;
+import android.os.Process;
 import android.os.RemoteException;
+import android.os.UserHandle;
+import android.provider.Settings;
 import android.text.TextUtils;
 import android.util.Log;
 
@@ -38,6 +46,7 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.List;
 
 /**
  * System implementation of MediaSessionManager
@@ -48,15 +57,17 @@
 
     private final SessionManagerImpl mSessionManagerImpl;
     private final MediaRouteProviderWatcher mRouteProviderWatcher;
+    private final MediaSessionStack mPriorityStack;
 
-    private final ArrayList<MediaSessionRecord> mSessions
-            = new ArrayList<MediaSessionRecord>();
+    private final ArrayList<MediaSessionRecord> mRecords = new ArrayList<MediaSessionRecord>();
     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;
+
     // Used to keep track of the current request to show routes for a specific
     // session so we drop late callbacks properly.
     private int mShowRoutesRequestId = 0;
@@ -69,6 +80,7 @@
         mSessionManagerImpl = new SessionManagerImpl();
         mRouteProviderWatcher = new MediaRouteProviderWatcher(context, mProviderWatcherCallback,
                 mHandler, context.getUserId());
+        mPriorityStack = new MediaSessionStack();
     }
 
     @Override
@@ -121,6 +133,30 @@
         }
     }
 
+    public void updateSession(MediaSessionRecord record) {
+        synchronized (mLock) {
+            mPriorityStack.onSessionStateChange(record);
+            if (record.isSystemPriority()) {
+                if (record.isActive()) {
+                    if (mPrioritySession != null) {
+                        Log.w(TAG, "Replacing existing priority session with a new session");
+                    }
+                    mPrioritySession = record;
+                } else {
+                    if (mPrioritySession == record) {
+                        mPrioritySession = null;
+                    }
+                }
+            }
+        }
+    }
+
+    public void onSessionPlaystateChange(MediaSessionRecord record, int oldState, int newState) {
+        synchronized (mLock) {
+            mPriorityStack.onPlaystateChange(record, oldState, newState);
+        }
+    }
+
     @Override
     public void monitor() {
         synchronized (mLock) {
@@ -141,7 +177,11 @@
     }
 
     private void destroySessionLocked(MediaSessionRecord session) {
-        mSessions.remove(session);
+        mRecords.remove(session);
+        mPriorityStack.removeSession(session);
+        if (session == mPrioritySession) {
+            mPrioritySession = null;
+        }
     }
 
     private void enforcePackageName(String packageName, int uid) {
@@ -158,8 +198,64 @@
         throw new IllegalArgumentException("packageName is not owned by the calling process");
     }
 
+    protected void enforcePhoneStatePermission(int pid, int uid) {
+        if (getContext().checkPermission(android.Manifest.permission.MODIFY_PHONE_STATE, pid, uid)
+                != PackageManager.PERMISSION_GRANTED) {
+            throw new SecurityException("Must hold the MODIFY_PHONE_STATE permission.");
+        }
+    }
+
+    /**
+     * Checks a caller's authorization to register an IRemoteControlDisplay.
+     * Authorization is granted if one of the following is true:
+     * <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>
+     * </ul>
+     */
+    private void enforceMediaPermissions(ComponentName compName, int pid, int uid) {
+        if (getContext()
+                .checkPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL, pid, uid)
+                    != PackageManager.PERMISSION_GRANTED
+                && !isEnabledNotificationListener(compName)) {
+            throw new SecurityException("Missing permission to control media.");
+        }
+    }
+
+    private boolean isEnabledNotificationListener(ComponentName compName) {
+        if (compName != null) {
+            final int currentUser = ActivityManager.getCurrentUser();
+            final String enabledNotifListeners = Settings.Secure.getStringForUser(
+                    getContext().getContentResolver(),
+                    Settings.Secure.ENABLED_NOTIFICATION_LISTENERS,
+                    currentUser);
+            if (enabledNotifListeners != null) {
+                final String[] components = enabledNotifListeners.split(":");
+                for (int i = 0; i < components.length; i++) {
+                    final ComponentName component =
+                            ComponentName.unflattenFromString(components[i]);
+                    if (component != null) {
+                        if (compName.equals(component)) {
+                            if (DEBUG) {
+                                Log.d(TAG, "ok to get sessions: " + component +
+                                        " is authorized notification listener");
+                            }
+                            return true;
+                        }
+                    }
+                }
+            }
+            if (DEBUG) {
+                Log.d(TAG, "not ok to get sessions, " + compName +
+                        " is not in list of ENABLED_NOTIFICATION_LISTENERS");
+            }
+        }
+        return false;
+    }
+
     private MediaSessionRecord createSessionInternal(int pid, String packageName,
-            ISessionCallback cb, String tag) {
+            ISessionCallback cb, String tag, boolean forCalls) {
         synchronized (mLock) {
             return createSessionLocked(pid, packageName, cb, tag);
         }
@@ -174,13 +270,24 @@
         } catch (RemoteException e) {
             throw new RuntimeException("Media Session owner died prematurely.", e);
         }
-        mSessions.add(session);
+        mRecords.add(session);
+        mPriorityStack.addSession(session);
         if (DEBUG) {
             Log.d(TAG, "Created session for package " + packageName + " with tag " + tag);
         }
         return session;
     }
 
+    private int findIndexOfSessionForIdLocked(String sessionId) {
+        for (int i = mRecords.size() - 1; i >= 0; i--) {
+            MediaSessionRecord session = mRecords.get(i);
+            if (TextUtils.equals(session.getSessionInfo().getId(), sessionId)) {
+                return i;
+            }
+        }
+        return -1;
+    }
+
     private MediaRouteProviderProxy getProviderLocked(String providerId) {
         for (int i = mProviders.size() - 1; i >= 0; i--) {
             MediaRouteProviderProxy provider = mProviders.get(i);
@@ -191,14 +298,9 @@
         return null;
     }
 
-    private int findIndexOfSessionForIdLocked(String sessionId) {
-        for (int i = mSessions.size() - 1; i >= 0; i--) {
-            MediaSessionRecord session = mSessions.get(i);
-            if (TextUtils.equals(session.getSessionInfo().getId(), sessionId)) {
-                return i;
-            }
-        }
-        return -1;
+    private boolean isSessionDiscoverable(MediaSessionRecord record) {
+        // TODO probably want to check more than if it's published.
+        return record.isActive();
     }
 
     private MediaRouteProviderWatcher.Callback mProviderWatcherCallback
@@ -232,7 +334,7 @@
             synchronized (mLock) {
                 int index = findIndexOfSessionForIdLocked(sessionId);
                 if (index != -1 && routes != null && routes.size() > 0) {
-                    MediaSessionRecord record = mSessions.get(index);
+                    MediaSessionRecord record = mRecords.get(index);
                     record.selectRoute(routes.get(0));
                 }
             }
@@ -244,7 +346,7 @@
             synchronized (mLock) {
                 int index = findIndexOfSessionForIdLocked(sessionId);
                 if (index != -1) {
-                    MediaSessionRecord session = mSessions.get(index);
+                    MediaSessionRecord session = mRecords.get(index);
                     session.setRouteConnected(route, options.getConnectionOptions(), connection);
                 }
             }
@@ -266,7 +368,37 @@
                 if (cb == null) {
                     throw new IllegalArgumentException("Controller callback cannot be null");
                 }
-                return createSessionInternal(pid, packageName, cb, tag).getSessionBinder();
+                return createSessionInternal(pid, packageName, cb, tag, false).getSessionBinder();
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        @Override
+        public List<IBinder> getSessions(ComponentName componentName) {
+            final int pid = Binder.getCallingPid();
+            final int uid = Binder.getCallingUid();
+            final long token = Binder.clearCallingIdentity();
+
+            try {
+                if (componentName != null) {
+                    // If they gave us a component name verify they own the
+                    // package
+                    enforcePackageName(componentName.getPackageName(), uid);
+                }
+                // Then check if they have the permissions or their component is
+                // allowed
+                enforceMediaPermissions(componentName, pid, uid);
+                ArrayList<IBinder> binders = new ArrayList<IBinder>();
+                synchronized (mLock) {
+                    ArrayList<MediaSessionRecord> records = mPriorityStack
+                            .getActiveSessions();
+                    int size = records.size();
+                    for (int i = 0; i < size; i++) {
+                        binders.add(records.get(i).getControllerBinder().asBinder());
+                    }
+                }
+                return binders;
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
@@ -286,13 +418,18 @@
             pw.println();
 
             synchronized (mLock) {
-                int count = mSessions.size();
-                pw.println("Sessions - have " + count + " states:");
-                for (int i = 0; i < count; i++) {
-                    MediaSessionRecord record = mSessions.get(i);
-                    pw.println();
-                    record.dump(pw, "");
+                pw.println("Session for calls:" + mPrioritySession);
+                if (mPrioritySession != null) {
+                    mPrioritySession.dump(pw, "");
                 }
+                int count = mRecords.size();
+                pw.println(count + " Sessions:");
+                for (int i = 0; i < count; i++) {
+                    mRecords.get(i).dump(pw, "");
+                    pw.println();
+                }
+                mPriorityStack.dumpLocked(pw, "");
+
                 pw.println("Providers:");
                 count = mProviders.size();
                 for (int i = 0; i < count; i++) {
diff --git a/services/core/java/com/android/server/media/MediaSessionStack.java b/services/core/java/com/android/server/media/MediaSessionStack.java
new file mode 100644
index 0000000..f9f004d
--- /dev/null
+++ b/services/core/java/com/android/server/media/MediaSessionStack.java
@@ -0,0 +1,267 @@
+/*
+ * 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.media;
+
+import android.media.session.PlaybackState;
+import android.media.session.Session;
+import android.text.TextUtils;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+
+/**
+ * Keeps track of media sessions and their priority for notifications, media
+ * button routing, etc.
+ */
+public class MediaSessionStack {
+    /**
+     * These are states that usually indicate the user took an action and should
+     * bump priority regardless of the old state.
+     */
+    private static final int[] ALWAYS_PRIORITY_STATES = {
+            PlaybackState.PLAYSTATE_FAST_FORWARDING,
+            PlaybackState.PLAYSTATE_REWINDING,
+            PlaybackState.PLAYSTATE_SKIPPING_BACKWARDS,
+            PlaybackState.PLAYSTATE_SKIPPING_FORWARDS };
+    /**
+     * These are states that usually indicate the user took an action if they
+     * were entered from a non-priority state.
+     */
+    private static final int[] TRANSITION_PRIORITY_STATES = {
+            PlaybackState.PLAYSTATE_BUFFERING,
+            PlaybackState.PLAYSTATE_CONNECTING,
+            PlaybackState.PLAYSTATE_PLAYING };
+
+    private final ArrayList<MediaSessionRecord> mSessions = new ArrayList<MediaSessionRecord>();
+
+    private MediaSessionRecord mCachedButtonReceiver;
+    private MediaSessionRecord mCachedDefault;
+    private ArrayList<MediaSessionRecord> mCachedActiveList;
+    private ArrayList<MediaSessionRecord> mCachedTransportControlList;
+
+    /**
+     * Add a record to the priority tracker.
+     *
+     * @param record The record to add.
+     */
+    public void addSession(MediaSessionRecord record) {
+        mSessions.add(record);
+        clearCache();
+    }
+
+    /**
+     * Remove a record from the priority tracker.
+     *
+     * @param record The record to remove.
+     */
+    public void removeSession(MediaSessionRecord record) {
+        mSessions.remove(record);
+        clearCache();
+    }
+
+    /**
+     * Notify the priority tracker that a session's state changed.
+     *
+     * @param record The record that changed.
+     * @param oldState Its old playback state.
+     * @param newState Its new playback state.
+     */
+    public void onPlaystateChange(MediaSessionRecord record, int oldState, int newState) {
+        if (shouldUpdatePriority(oldState, newState)) {
+            mSessions.remove(record);
+            mSessions.add(0, record);
+            clearCache();
+        }
+    }
+
+    /**
+     * Handle any stack changes that need to occur in response to a session
+     * state change. TODO add the old and new session state as params
+     *
+     * @param record The record that changed.
+     */
+    public void onSessionStateChange(MediaSessionRecord record) {
+        // For now just clear the cache. Eventually we'll selectively clear
+        // depending on what changed.
+        clearCache();
+    }
+
+    /**
+     * Get the current priority sorted list of active sessions. The most
+     * important session is at index 0 and the least important at size - 1.
+     *
+     * @return All the active sessions in priority order.
+     */
+    public ArrayList<MediaSessionRecord> getActiveSessions() {
+        if (mCachedActiveList == null) {
+            mCachedActiveList = getPriorityListLocked(true, 0);
+        }
+        return mCachedActiveList;
+    }
+
+    /**
+     * Get the current priority sorted list of active sessions that use
+     * transport controls. The most important session is at index 0 and the
+     * least important at size -1.
+     *
+     * @return All the active sessions that handle transport controls in
+     *         priority order.
+     */
+    public ArrayList<MediaSessionRecord> getTransportControlSessions() {
+        if (mCachedTransportControlList == null) {
+            mCachedTransportControlList = getPriorityListLocked(true,
+                    Session.FLAG_HANDLES_TRANSPORT_CONTROLS);
+        }
+        return mCachedTransportControlList;
+    }
+
+    /**
+     * Get the highest priority active session.
+     *
+     * @return The current highest priority session or null.
+     */
+    public MediaSessionRecord getDefaultSession() {
+        if (mCachedDefault != null) {
+            return mCachedDefault;
+        }
+        ArrayList<MediaSessionRecord> records = getPriorityListLocked(true, 0);
+        if (records.size() > 0) {
+            return records.get(0);
+        }
+        return null;
+    }
+
+    /**
+     * Get the highest priority session that can handle media buttons.
+     *
+     * @return The default media button session or null.
+     */
+    public MediaSessionRecord getDefaultMediaButtonSession() {
+        if (mCachedButtonReceiver != null) {
+            return mCachedButtonReceiver;
+        }
+        ArrayList<MediaSessionRecord> records = getPriorityListLocked(true,
+                Session.FLAG_HANDLES_MEDIA_BUTTONS);
+        if (records.size() > 0) {
+            mCachedButtonReceiver = records.get(0);
+        }
+        return mCachedButtonReceiver;
+    }
+
+    public void dumpLocked(PrintWriter pw, String prefix) {
+        ArrayList<MediaSessionRecord> sortedSessions = getPriorityListLocked(false, 0);
+        int count = sortedSessions.size();
+        pw.println(prefix + "Sessions Stack - have " + count + " sessions:");
+        String indent = prefix + "  ";
+        for (int i = 0; i < count; i++) {
+            MediaSessionRecord record = sortedSessions.get(i);
+            record.dump(pw, indent);
+            pw.println();
+        }
+    }
+
+    /**
+     * Get a priority sorted list of sessions. Can filter to only return active
+     * sessions or sessions with specific flags.
+     *
+     * @param activeOnly True to only return active sessions, false to return
+     *            all sessions.
+     * @param withFlags Only return sessions with all the specified flags set. 0
+     *            returns all sessions.
+     * @return The priority sorted list of sessions.
+     */
+    private ArrayList<MediaSessionRecord> getPriorityListLocked(boolean activeOnly, int withFlags) {
+        ArrayList<MediaSessionRecord> result = new ArrayList<MediaSessionRecord>();
+        int lastLocalIndex = 0;
+        int lastActiveIndex = 0;
+        int lastPublishedIndex = 0;
+
+        int size = mSessions.size();
+        for (int i = 0; i < size; i++) {
+            final MediaSessionRecord session = mSessions.get(i);
+
+            if ((session.getFlags() & withFlags) != withFlags) {
+                continue;
+            }
+            if (!session.isActive()) {
+                if (!activeOnly) {
+                    // If we're getting unpublished as well always put them at
+                    // the end
+                    result.add(session);
+                }
+                continue;
+            }
+
+            if (session.isSystemPriority()) {
+                // System priority sessions are special and always go at the
+                // front. We expect there to only be one of these at a time.
+                result.add(0, session);
+                lastLocalIndex++;
+                lastActiveIndex++;
+                lastPublishedIndex++;
+            } else if (session.isPlaybackActive()) {
+                // TODO replace getRoute() == null with real local route check
+                if(session.getRoute() == null) {
+                    // Active local sessions get top priority
+                    result.add(lastLocalIndex, session);
+                    lastLocalIndex++;
+                    lastActiveIndex++;
+                    lastPublishedIndex++;
+                } else {
+                    // Then active remote sessions
+                    result.add(lastActiveIndex, session);
+                    lastActiveIndex++;
+                    lastPublishedIndex++;
+                }
+            } else {
+                // inactive sessions go at the end in order of whoever last did
+                // something.
+                result.add(lastPublishedIndex, session);
+                lastPublishedIndex++;
+            }
+        }
+
+        return result;
+    }
+
+    private boolean shouldUpdatePriority(int oldState, int newState) {
+        if (containsState(newState, ALWAYS_PRIORITY_STATES)) {
+            return true;
+        }
+        if (!containsState(oldState, TRANSITION_PRIORITY_STATES)
+                && containsState(newState, TRANSITION_PRIORITY_STATES)) {
+            return true;
+        }
+        return false;
+    }
+
+    private boolean containsState(int state, int[] states) {
+        for (int i = 0; i < states.length; i++) {
+            if (states[i] == state) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private void clearCache() {
+        mCachedDefault = null;
+        mCachedButtonReceiver = null;
+        mCachedActiveList = null;
+        mCachedTransportControlList = null;
+    }
+}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 6a843a8..72fc295 100755
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -7479,11 +7479,7 @@
                 null);
         PackageSetting pkgSetting;
         final int uid = Binder.getCallingUid();
-        if (UserHandle.getUserId(uid) != userId) {
-            mContext.enforceCallingPermission(
-                    android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
-                    "installExistingPackage for user " + userId);
-        }
+        enforceCrossUserPermission(uid, userId, true, "installExistingPackage for user " + userId);
         if (isUserRestricted(userId, UserManager.DISALLOW_INSTALL_APPS)) {
             return PackageManager.INSTALL_FAILED_USER_RESTRICTED;
         }
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 3239b46..4f5326f 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -734,6 +734,15 @@
                 writeBoolean(serializer, restrictions, UserManager.DISALLOW_USB_FILE_TRANSFER);
                 writeBoolean(serializer, restrictions, UserManager.DISALLOW_CONFIG_CREDENTIALS);
                 writeBoolean(serializer, restrictions, UserManager.DISALLOW_REMOVE_USER);
+                writeBoolean(serializer, restrictions, UserManager.DISALLOW_DEBUGGING_FEATURES);
+                writeBoolean(serializer, restrictions, UserManager.DISALLOW_CONFIG_VPN);
+                writeBoolean(serializer, restrictions, UserManager.DISALLOW_CONFIG_TETHERING);
+                writeBoolean(serializer, restrictions, UserManager.DISALLOW_FACTORY_RESET);
+                writeBoolean(serializer, restrictions, UserManager.DISALLOW_ADD_USER);
+                writeBoolean(serializer, restrictions, UserManager.ENSURE_VERIFY_APPS);
+                writeBoolean(serializer, restrictions, UserManager.DISALLOW_CONFIG_CELL_BROADCASTS);
+                writeBoolean(serializer, restrictions, UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS);
+                writeBoolean(serializer, restrictions, UserManager.DISALLOW_CONFIG_APPS);
                 serializer.endTag(null, TAG_RESTRICTIONS);
             }
             serializer.endTag(null, TAG_USER);
@@ -873,6 +882,15 @@
                         readBoolean(parser, restrictions, UserManager.DISALLOW_USB_FILE_TRANSFER);
                         readBoolean(parser, restrictions, UserManager.DISALLOW_CONFIG_CREDENTIALS);
                         readBoolean(parser, restrictions, UserManager.DISALLOW_REMOVE_USER);
+                        readBoolean(parser, restrictions, UserManager.DISALLOW_DEBUGGING_FEATURES);
+                        readBoolean(parser, restrictions, UserManager.DISALLOW_CONFIG_VPN);
+                        readBoolean(parser, restrictions, UserManager.DISALLOW_CONFIG_TETHERING);
+                        readBoolean(parser, restrictions, UserManager.DISALLOW_FACTORY_RESET);
+                        readBoolean(parser, restrictions, UserManager.DISALLOW_ADD_USER);
+                        readBoolean(parser, restrictions, UserManager.ENSURE_VERIFY_APPS);
+                        readBoolean(parser, restrictions, UserManager.DISALLOW_CONFIG_CELL_BROADCASTS);
+                        readBoolean(parser, restrictions, UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS);
+                        readBoolean(parser, restrictions, UserManager.DISALLOW_CONFIG_APPS);
                     }
                 }
             }
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 022bdae..738ad32 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -74,6 +74,7 @@
     private boolean mMenuVisible = false;
     private int mImeWindowVis = 0;
     private int mImeBackDisposition;
+    private boolean mShowImeSwitcher;
     private IBinder mImeToken = null;
     private int mCurrentUserId;
 
@@ -346,7 +347,8 @@
     }
 
     @Override
-    public void setImeWindowStatus(final IBinder token, final int vis, final int backDisposition) {
+    public void setImeWindowStatus(final IBinder token, final int vis, final int backDisposition,
+            final boolean showImeSwitcher) {
         enforceStatusBar();
 
         if (SPEW) {
@@ -360,11 +362,12 @@
             mImeWindowVis = vis;
             mImeBackDisposition = backDisposition;
             mImeToken = token;
+            mShowImeSwitcher = showImeSwitcher;
             mHandler.post(new Runnable() {
                 public void run() {
                     if (mBar != null) {
                         try {
-                            mBar.setImeWindowStatus(token, vis, backDisposition);
+                            mBar.setImeWindowStatus(token, vis, backDisposition, showImeSwitcher);
                         } catch (RemoteException ex) {
                         }
                     }
@@ -512,6 +515,7 @@
             switches[2] = mMenuVisible ? 1 : 0;
             switches[3] = mImeWindowVis;
             switches[4] = mImeBackDisposition;
+            switches[7] = mShowImeSwitcher ? 1 : 0;
             binders.add(mImeToken);
         }
         switches[5] = mWindowManager.isHardKeyboardAvailable() ? 1 : 0;
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 7a0d1c7..dcca837 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -44,12 +44,13 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.content.pm.UserInfo;
-import android.net.ProxyProperties;
+import android.net.ProxyInfo;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.Environment;
@@ -2532,7 +2533,7 @@
         exclusionList = exclusionList.trim();
         ContentResolver res = mContext.getContentResolver();
 
-        ProxyProperties proxyProperties = new ProxyProperties(data[0], proxyPort, exclusionList);
+        ProxyInfo proxyProperties = new ProxyInfo(data[0], proxyPort, exclusionList);
         if (!proxyProperties.isValid()) {
             Slog.e(LOG_TAG, "Invalid proxy properties, ignoring: " + proxyProperties.toString());
             return;
@@ -3023,6 +3024,7 @@
         }
     }
 
+    @Override
     public void addPersistentPreferredActivity(ComponentName who, IntentFilter filter,
             ComponentName activity) {
         synchronized (this) {
@@ -3043,6 +3045,7 @@
         }
     }
 
+    @Override
     public void clearPackagePersistentPreferredActivities(ComponentName who, String packageName) {
         synchronized (this) {
             if (who == null) {
@@ -3168,4 +3171,110 @@
             }
         }
     }
+
+    @Override
+    public void enableSystemApp(ComponentName who, String packageName) {
+        synchronized (this) {
+            if (who == null) {
+                throw new NullPointerException("ComponentName is null");
+            }
+
+            getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+
+            int userId = UserHandle.getCallingUserId();
+            long id = Binder.clearCallingIdentity();
+
+            try {
+                UserManager um = UserManager.get(mContext);
+                if (!um.getUserInfo(userId).isManagedProfile()) {
+                    throw new IllegalStateException(
+                            "Only call this method from a managed profile.");
+                }
+
+                // TODO: Use UserManager::getProfileParent when available.
+                UserInfo primaryUser = um.getUserInfo(UserHandle.USER_OWNER);
+
+                if (DBG) {
+                    Slog.v(LOG_TAG, "installing " + packageName + " for "
+                            + userId);
+                }
+
+                IPackageManager pm = AppGlobals.getPackageManager();
+                if (!isSystemApp(pm, packageName, primaryUser.id)) {
+                    throw new IllegalArgumentException("Only system apps can be enabled this way.");
+                }
+
+                // Install the app.
+                pm.installExistingPackageAsUser(packageName, userId);
+
+            } catch (RemoteException re) {
+                // shouldn't happen
+                Slog.wtf(LOG_TAG, "Failed to install " + packageName, re);
+            } finally {
+                restoreCallingIdentity(id);
+            }
+        }
+    }
+
+    @Override
+    public int enableSystemAppWithIntent(ComponentName who, Intent intent) {
+        synchronized (this) {
+            if (who == null) {
+                throw new NullPointerException("ComponentName is null");
+            }
+
+            getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+
+            int userId = UserHandle.getCallingUserId();
+            long id = Binder.clearCallingIdentity();
+
+            try {
+                UserManager um = UserManager.get(mContext);
+                if (!um.getUserInfo(userId).isManagedProfile()) {
+                    throw new IllegalStateException(
+                            "Only call this method from a managed profile.");
+                }
+
+                // TODO: Use UserManager::getProfileParent when available.
+                UserInfo primaryUser = um.getUserInfo(UserHandle.USER_OWNER);
+
+                IPackageManager pm = AppGlobals.getPackageManager();
+                List<ResolveInfo> activitiesToEnable = pm.queryIntentActivities(intent,
+                        intent.resolveTypeIfNeeded(mContext.getContentResolver()),
+                        0, // no flags
+                        primaryUser.id);
+
+                if (DBG) Slog.d(LOG_TAG, "Enabling system activities: " + activitiesToEnable);
+                int numberOfAppsInstalled = 0;
+                if (activitiesToEnable != null) {
+                    for (ResolveInfo info : activitiesToEnable) {
+                        if (info.activityInfo != null) {
+
+                            if (!isSystemApp(pm, info.activityInfo.packageName, primaryUser.id)) {
+                                throw new IllegalArgumentException(
+                                        "Only system apps can be enabled this way.");
+                            }
+
+
+                            numberOfAppsInstalled++;
+                            pm.installExistingPackageAsUser(info.activityInfo.packageName, userId);
+                        }
+                    }
+                }
+                return numberOfAppsInstalled;
+            } catch (RemoteException e) {
+                // shouldn't happen
+                Slog.wtf(LOG_TAG, "Failed to resolve intent for: " + intent);
+                return 0;
+            } finally {
+                restoreCallingIdentity(id);
+            }
+        }
+    }
+
+    private boolean isSystemApp(IPackageManager pm, String packageName, int userId)
+            throws RemoteException {
+        ApplicationInfo appInfo = pm.getApplicationInfo(packageName, 0, userId);
+        return (appInfo.flags & ApplicationInfo.FLAG_SYSTEM) > 0;
+    }
 }
diff --git a/tests/OneMedia/src/com/android/onemedia/PlayerSession.java b/tests/OneMedia/src/com/android/onemedia/PlayerSession.java
index 2e029f0..b7dcef7 100644
--- a/tests/OneMedia/src/com/android/onemedia/PlayerSession.java
+++ b/tests/OneMedia/src/com/android/onemedia/PlayerSession.java
@@ -84,11 +84,12 @@
         Log.d(TAG, "Creating session for package " + mContext.getBasePackageName());
         mSession = man.createSession("OneMedia");
         mSession.addCallback(mCallback);
-        mPerformer = mSession.setTransportPerformerEnabled();
+        mPerformer = mSession.getTransportPerformer();
         mPerformer.addListener(new TransportListener());
         mPerformer.setPlaybackState(mPlaybackState);
+        mSession.setFlags(Session.FLAG_HANDLES_TRANSPORT_CONTROLS);
         mSession.setRouteOptions(mRouteOptions);
-        mSession.publish();
+        mSession.setActive(true);
     }
 
     public void onDestroy() {