Merge "Fixed race condition regarding first child max height"
diff --git a/Android.mk b/Android.mk
index be7e055..232f5bf 100644
--- a/Android.mk
+++ b/Android.mk
@@ -180,6 +180,8 @@
 	core/java/android/os/IUserManager.aidl \
 	core/java/android/os/IVibratorService.aidl \
 	core/java/android/service/notification/INotificationListener.aidl \
+	core/java/android/service/notification/IConditionListener.aidl \
+	core/java/android/service/notification/IConditionProvider.aidl \
 	core/java/android/print/ILayoutResultCallback.aidl \
 	core/java/android/print/IPrinterDiscoveryObserver.aidl \
 	core/java/android/print/IPrintDocumentAdapter.aidl \
@@ -197,6 +199,9 @@
 	core/java/android/service/dreams/IDreamService.aidl \
 	core/java/android/service/trust/ITrustAgentService.aidl \
 	core/java/android/service/trust/ITrustAgentServiceCallback.aidl \
+	core/java/android/service/voice/IVoiceInteractionService.aidl \
+	core/java/android/service/voice/IVoiceInteractionSession.aidl \
+	core/java/android/service/voice/IVoiceInteractionSessionService.aidl \
 	core/java/android/service/wallpaper/IWallpaperConnection.aidl \
 	core/java/android/service/wallpaper/IWallpaperEngine.aidl \
 	core/java/android/service/wallpaper/IWallpaperService.aidl \
@@ -230,6 +235,10 @@
 	core/java/com/android/internal/app/IBatteryStats.aidl \
 	core/java/com/android/internal/app/IProcessStats.aidl \
 	core/java/com/android/internal/app/IUsageStats.aidl \
+	core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl \
+	core/java/com/android/internal/app/IVoiceInteractor.aidl \
+	core/java/com/android/internal/app/IVoiceInteractorCallback.aidl \
+	core/java/com/android/internal/app/IVoiceInteractorRequest.aidl \
 	core/java/com/android/internal/app/IMediaContainerService.aidl \
 	core/java/com/android/internal/appwidget/IAppWidgetService.aidl \
 	core/java/com/android/internal/appwidget/IAppWidgetHost.aidl \
diff --git a/api/current.txt b/api/current.txt
index 30c9f98..34b1ac8 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -31,6 +31,7 @@
     field public static final java.lang.String BIND_TEXT_SERVICE = "android.permission.BIND_TEXT_SERVICE";
     field public static final java.lang.String BIND_TRUST_AGENT_SERVICE = "android.permission.BIND_TRUST_AGENT_SERVICE";
     field public static final java.lang.String BIND_TV_INPUT = "android.permission.BIND_TV_INPUT";
+    field public static final java.lang.String BIND_VOICE_INTERACTION = "android.permission.BIND_VOICE_INTERACTION";
     field public static final java.lang.String BIND_VPN_SERVICE = "android.permission.BIND_VPN_SERVICE";
     field public static final java.lang.String BIND_WALLPAPER = "android.permission.BIND_WALLPAPER";
     field public static final java.lang.String BLUETOOTH = "android.permission.BLUETOOTH";
@@ -1008,6 +1009,7 @@
     field public static final int selectedDateVerticalBar = 16843591; // 0x1010347
     field public static final int selectedWeekBackgroundColor = 16843586; // 0x1010342
     field public static final int sequence = 16843815; // 0x1010427
+    field public static final int sessionService = 16843850; // 0x101044a
     field public static final int settingsActivity = 16843301; // 0x1010225
     field public static final int shadowColor = 16843105; // 0x1010161
     field public static final int shadowDx = 16843106; // 0x1010162
@@ -1662,10 +1664,14 @@
     field public static final int decelerate_cubic = 17563651; // 0x10c0003
     field public static final int decelerate_quad = 17563649; // 0x10c0001
     field public static final int decelerate_quint = 17563653; // 0x10c0005
-    field public static final int fast_out_linear_in = 17563663; // 0x10c000f
-    field public static final int fast_out_slow_in = 17563661; // 0x10c000d
+    field public static final int fast_out_linear_in = 17563667; // 0x10c0013
+    field public static final int fast_out_slow_in = 17563665; // 0x10c0011
+    field public static final int l_resource_pad1 = 17563664; // 0x10c0010
+    field public static final int l_resource_pad2 = 17563663; // 0x10c000f
+    field public static final int l_resource_pad3 = 17563662; // 0x10c000e
+    field public static final int l_resource_pad4 = 17563661; // 0x10c000d
     field public static final int linear = 17563659; // 0x10c000b
-    field public static final int linear_out_slow_in = 17563662; // 0x10c000e
+    field public static final int linear_out_slow_in = 17563666; // 0x10c0012
     field public static final int overshoot = 17563656; // 0x10c0008
   }
 
@@ -3202,6 +3208,7 @@
     method public int getTaskId();
     method public final java.lang.CharSequence getTitle();
     method public final int getTitleColor();
+    method public android.app.VoiceInteractor getVoiceInteractor();
     method public final int getVolumeControlStream();
     method public android.view.Window getWindow();
     method public android.view.WindowManager getWindowManager();
@@ -3213,6 +3220,7 @@
     method public boolean isFinishing();
     method public boolean isImmersive();
     method public boolean isTaskRoot();
+    method public boolean isVoiceInteraction();
     method public final deprecated android.database.Cursor managedQuery(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String);
     method public boolean moveTaskToBack(boolean);
     method public boolean navigateUpTo(android.content.Intent);
@@ -4838,6 +4846,29 @@
     field public static final int MODE_NIGHT_YES = 2; // 0x2
   }
 
+  public class VoiceInteractor {
+    method public boolean submitRequest(android.app.VoiceInteractor.Request);
+    method public boolean[] supportsCommands(java.lang.String[]);
+  }
+
+  public static class VoiceInteractor.CommandRequest extends android.app.VoiceInteractor.Request {
+    ctor public VoiceInteractor.CommandRequest(java.lang.String, android.os.Bundle);
+    method public void onCommandResult(android.os.Bundle);
+  }
+
+  public static class VoiceInteractor.ConfirmationRequest extends android.app.VoiceInteractor.Request {
+    ctor public VoiceInteractor.ConfirmationRequest(java.lang.CharSequence, android.os.Bundle);
+    method public void onConfirmationResult(boolean, android.os.Bundle);
+  }
+
+  public static abstract class VoiceInteractor.Request {
+    ctor public VoiceInteractor.Request();
+    method public void cancel();
+    method public android.app.Activity getActivity();
+    method public android.content.Context getContext();
+    method public void onCancel();
+  }
+
   public final class WallpaperInfo implements android.os.Parcelable {
     ctor public WallpaperInfo(android.content.Context, android.content.pm.ResolveInfo) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
     method public int describeContents();
@@ -4947,7 +4978,9 @@
 
   public class DevicePolicyManager {
     method public void addPersistentPreferredActivity(android.content.ComponentName, android.content.IntentFilter, android.content.ComponentName);
+    method public void addUserRestriction(android.content.ComponentName, java.lang.String);
     method public void clearPackagePersistentPreferredActivities(android.content.ComponentName, java.lang.String);
+    method public void clearUserRestriction(android.content.ComponentName, java.lang.String);
     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);
@@ -5010,6 +5043,9 @@
     field public static final int KEYGUARD_DISABLE_FEATURES_ALL = 2147483647; // 0x7fffffff
     field public static final int KEYGUARD_DISABLE_FEATURES_NONE = 0; // 0x0
     field public static final int KEYGUARD_DISABLE_SECURE_CAMERA = 2; // 0x2
+    field public static final int KEYGUARD_DISABLE_SECURE_NOTIFICATIONS = 4; // 0x4
+    field public static final int KEYGUARD_DISABLE_TRUST_AGENTS = 16; // 0x10
+    field public static final int KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS = 8; // 0x8
     field public static final int KEYGUARD_DISABLE_WIDGETS_ALL = 1; // 0x1
     field public static final int PASSWORD_QUALITY_ALPHABETIC = 262144; // 0x40000
     field public static final int PASSWORD_QUALITY_ALPHANUMERIC = 327680; // 0x50000
@@ -6997,6 +7033,7 @@
     field public static final java.lang.String CATEGORY_TAB = "android.intent.category.TAB";
     field public static final java.lang.String CATEGORY_TEST = "android.intent.category.TEST";
     field public static final java.lang.String CATEGORY_UNIT_TEST = "android.intent.category.UNIT_TEST";
+    field public static final java.lang.String CATEGORY_VOICE = "android.intent.category.VOICE";
     field public static final android.os.Parcelable.Creator CREATOR;
     field public static final java.lang.String EXTRA_ALARM_COUNT = "android.intent.extra.ALARM_COUNT";
     field public static final java.lang.String EXTRA_ALLOW_MULTIPLE = "android.intent.extra.ALLOW_MULTIPLE";
@@ -7712,6 +7749,7 @@
     method public long getFirstInstallTime();
     method public android.graphics.drawable.Drawable getIcon(int);
     method public java.lang.CharSequence getLabel();
+    method public java.lang.String getName();
     method public android.os.UserHandle getUser();
   }
 
@@ -10023,11 +10061,18 @@
     method public void setPaint(android.graphics.Paint);
   }
 
-  public class Outline {
+  public final class Outline {
     ctor public Outline();
-    method public final boolean isValid();
+    ctor public Outline(android.graphics.Outline);
+    method public boolean isValid();
     method public void set(android.graphics.Outline);
+    method public void setConvexPath(android.graphics.Path);
+    method public void setOval(int, int, int, int);
+    method public void setOval(android.graphics.Rect);
+    method public void setRect(int, int, int, int);
+    method public void setRect(android.graphics.Rect);
     method public void setRoundRect(int, int, int, int, float);
+    method public void setRoundRect(android.graphics.Rect, float);
   }
 
   public class Paint {
@@ -10196,6 +10241,7 @@
     method public void addArc(android.graphics.RectF, float, float);
     method public void addCircle(float, float, float, android.graphics.Path.Direction);
     method public void addOval(android.graphics.RectF, android.graphics.Path.Direction);
+    method public void addOval(float, float, float, float, android.graphics.Path.Direction);
     method public void addPath(android.graphics.Path, float, float);
     method public void addPath(android.graphics.Path);
     method public void addPath(android.graphics.Path, android.graphics.Matrix);
@@ -10750,7 +10796,7 @@
     method public int getMinimumHeight();
     method public int getMinimumWidth();
     method public abstract int getOpacity();
-    method public android.graphics.Outline getOutline();
+    method public boolean getOutline(android.graphics.Outline);
     method public boolean getPadding(android.graphics.Rect);
     method public int[] getState();
     method public android.graphics.Region getTransparentRegion();
@@ -11123,6 +11169,7 @@
     method public android.graphics.drawable.shapes.Shape clone() throws java.lang.CloneNotSupportedException;
     method public abstract void draw(android.graphics.Canvas, android.graphics.Paint);
     method public final float getHeight();
+    method public boolean getOutline(android.graphics.Outline);
     method public final float getWidth();
     method public boolean hasAlpha();
     method protected void onResize(float, float);
@@ -24878,6 +24925,42 @@
 
 }
 
+package android.service.voice {
+
+  public class VoiceInteractionService extends android.app.Service {
+    ctor public VoiceInteractionService();
+    method public android.os.IBinder onBind(android.content.Intent);
+    method public void startVoiceActivity(android.content.Intent, android.os.Bundle);
+    field public static final java.lang.String SERVICE_INTERFACE = "android.service.voice.VoiceInteractionService";
+    field public static final java.lang.String SERVICE_META_DATA = "android.voice_interaction";
+  }
+
+  public abstract class VoiceInteractionSession {
+    ctor public VoiceInteractionSession(android.content.Context);
+    ctor public VoiceInteractionSession(android.content.Context, android.os.Handler);
+    method public abstract void onCancel(android.service.voice.VoiceInteractionSession.Request);
+    method public abstract void onCommand(android.service.voice.VoiceInteractionSession.Caller, android.service.voice.VoiceInteractionSession.Request, java.lang.String, android.os.Bundle);
+    method public abstract void onConfirm(android.service.voice.VoiceInteractionSession.Caller, android.service.voice.VoiceInteractionSession.Request, java.lang.String, android.os.Bundle);
+    method public abstract boolean[] onGetSupportedCommands(android.service.voice.VoiceInteractionSession.Caller, java.lang.String[]);
+  }
+
+  public static class VoiceInteractionSession.Caller {
+  }
+
+  public static class VoiceInteractionSession.Request {
+    method public void sendCancelResult();
+    method public void sendCommandResult(boolean, android.os.Bundle);
+    method public void sendConfirmResult(boolean, android.os.Bundle);
+  }
+
+  public abstract class VoiceInteractionSessionService extends android.app.Service {
+    ctor public VoiceInteractionSessionService();
+    method public android.os.IBinder onBind(android.content.Intent);
+    method public abstract android.service.voice.VoiceInteractionSession onNewSession(android.os.Bundle);
+  }
+
+}
+
 package android.service.wallpaper {
 
   public abstract class WallpaperService extends android.app.Service {
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index 7df55a5..6b55b7b 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -749,6 +749,11 @@
                             "Error: Activity not started, you do not "
                             + "have permission to access it.");
                     break;
+                case ActivityManager.START_NOT_VOICE_COMPATIBLE:
+                    out.println(
+                            "Error: Activity not started, voice control not allowed for: "
+                                    + intent);
+                    break;
                 default:
                     out.println(
                             "Error: Activity not started, unknown error code " + res);
diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp
index 41afa39..1780e03 100644
--- a/cmds/bootanimation/BootAnimation.cpp
+++ b/cmds/bootanimation/BootAnimation.cpp
@@ -51,6 +51,7 @@
 
 #include "BootAnimation.h"
 
+#define OEM_BOOTANIMATION_FILE "/oem/media/bootanimation.zip"
 #define SYSTEM_BOOTANIMATION_FILE "/system/media/bootanimation.zip"
 #define SYSTEM_ENCRYPTED_BOOTANIMATION_FILE "/system/media/bootanimation-encrypted.zip"
 #define EXIT_PROP_NAME "service.bootanim.exit"
@@ -283,6 +284,9 @@
             (access(SYSTEM_ENCRYPTED_BOOTANIMATION_FILE, R_OK) == 0) &&
             ((zipFile = ZipFileRO::open(SYSTEM_ENCRYPTED_BOOTANIMATION_FILE)) != NULL)) ||
 
+            ((access(OEM_BOOTANIMATION_FILE, R_OK) == 0) &&
+            ((zipFile = ZipFileRO::open(OEM_BOOTANIMATION_FILE)) != NULL)) ||
+
             ((access(SYSTEM_BOOTANIMATION_FILE, R_OK) == 0) &&
             ((zipFile = ZipFileRO::open(SYSTEM_BOOTANIMATION_FILE)) != NULL))) {
         mZip = zipFile;
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 599a608..8981c88 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -22,6 +22,7 @@
 import android.util.ArrayMap;
 import android.util.SuperNotCalledException;
 import android.widget.Toolbar;
+import com.android.internal.app.IVoiceInteractor;
 import com.android.internal.app.WindowDecorActionBar;
 import com.android.internal.app.ToolbarActionBar;
 import com.android.internal.policy.PolicyManager;
@@ -726,6 +727,8 @@
     /*package*/ ActionBar mActionBar = null;
     private boolean mEnableDefaultActionBarUp;
 
+    private VoiceInteractor mVoiceInteractor;
+
     private CharSequence mTitle;
     private int mTitleColor = 0;
 
@@ -1134,6 +1137,23 @@
     }
 
     /**
+     * Check whether this activity is running as part of a voice interaction with the user.
+     * If true, it should perform its interaction with the user through the
+     * {@link VoiceInteractor} returned by {@link #getVoiceInteractor}.
+     */
+    public boolean isVoiceInteraction() {
+        return mVoiceInteractor != null;
+    }
+
+    /**
+     * Retrieve the active {@link VoiceInteractor} that the user is going through to
+     * interact with this activity.
+     */
+    public VoiceInteractor getVoiceInteractor() {
+        return mVoiceInteractor;
+    }
+
+    /**
      * This is called for activities that set launchMode to "singleTop" in
      * their package, or if a client used the {@link Intent#FLAG_ACTIVITY_SINGLE_TOP}
      * flag when calling {@link #startActivity}.  In either case, when the
@@ -5397,7 +5417,7 @@
             NonConfigurationInstances lastNonConfigurationInstances,
             Configuration config) {
         attach(context, aThread, instr, token, ident, application, intent, info, title, parent, id,
-                lastNonConfigurationInstances, config, null);
+                lastNonConfigurationInstances, config, null, null);
     }
 
     final void attach(Context context, ActivityThread aThread,
@@ -5405,7 +5425,7 @@
             Application application, Intent intent, ActivityInfo info,
             CharSequence title, Activity parent, String id,
             NonConfigurationInstances lastNonConfigurationInstances,
-            Configuration config, Bundle options) {
+            Configuration config, Bundle options, IVoiceInteractor voiceInteractor) {
         attachBaseContext(context);
 
         mFragments.attachActivity(this, mContainer, null);
@@ -5433,6 +5453,8 @@
         mParent = parent;
         mEmbeddedID = id;
         mLastNonConfigurationInstances = lastNonConfigurationInstances;
+        mVoiceInteractor = voiceInteractor != null
+                ? new VoiceInteractor(this, this, voiceInteractor, Looper.myLooper()) : null;
 
         mWindow.setWindowManager(
                 (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index c027e99..018e949 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -76,6 +76,13 @@
     public static final String META_HOME_ALTERNATE = "android.app.home.alternate";
 
     /**
+     * Result for IActivityManager.startActivity: trying to start an activity under voice
+     * control when that activity does not support the VOICE category.
+     * @hide
+     */
+    public static final int START_NOT_VOICE_COMPATIBLE = -7;
+
+    /**
      * Result for IActivityManager.startActivity: an error where the
      * start had to be canceled.
      * @hide
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 10831f2..b1c37de 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -43,9 +43,11 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.StrictMode;
+import android.service.voice.IVoiceInteractionSession;
 import android.text.TextUtils;
 import android.util.Log;
 import android.util.Singleton;
+import com.android.internal.app.IVoiceInteractor;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -242,6 +244,33 @@
             return true;
         }
 
+        case START_VOICE_ACTIVITY_TRANSACTION:
+        {
+            data.enforceInterface(IActivityManager.descriptor);
+            String callingPackage = data.readString();
+            int callingPid = data.readInt();
+            int callingUid = data.readInt();
+            Intent intent = Intent.CREATOR.createFromParcel(data);
+            String resolvedType = data.readString();
+            IVoiceInteractionSession session = IVoiceInteractionSession.Stub.asInterface(
+                    data.readStrongBinder());
+            IVoiceInteractor interactor = IVoiceInteractor.Stub.asInterface(
+                    data.readStrongBinder());
+            int startFlags = data.readInt();
+            String profileFile = data.readString();
+            ParcelFileDescriptor profileFd = data.readInt() != 0
+                    ? ParcelFileDescriptor.CREATOR.createFromParcel(data) : null;
+            Bundle options = data.readInt() != 0
+                    ? Bundle.CREATOR.createFromParcel(data) : null;
+            int userId = data.readInt();
+            int result = startVoiceActivity(callingPackage, callingPid, callingUid,
+                    intent, resolvedType, session, interactor, startFlags,
+                    profileFile, profileFd, options, userId);
+            reply.writeNoException();
+            reply.writeInt(result);
+            return true;
+        }
+
         case START_NEXT_MATCHING_ACTIVITY_TRANSACTION:
         {
             data.enforceInterface(IActivityManager.descriptor);
@@ -2323,6 +2352,42 @@
         data.recycle();
         return result;
     }
+    public int startVoiceActivity(String callingPackage, int callingPid, int callingUid,
+            Intent intent, String resolvedType, IVoiceInteractionSession session,
+            IVoiceInteractor interactor, int startFlags, String profileFile,
+            ParcelFileDescriptor profileFd, Bundle options, int userId) throws RemoteException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeInterfaceToken(IActivityManager.descriptor);
+        data.writeString(callingPackage);
+        data.writeInt(callingPid);
+        data.writeInt(callingUid);
+        intent.writeToParcel(data, 0);
+        data.writeString(resolvedType);
+        data.writeStrongBinder(session.asBinder());
+        data.writeStrongBinder(interactor.asBinder());
+        data.writeInt(startFlags);
+        data.writeString(profileFile);
+        if (profileFd != null) {
+            data.writeInt(1);
+            profileFd.writeToParcel(data, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
+        } else {
+            data.writeInt(0);
+        }
+        if (options != null) {
+            data.writeInt(1);
+            options.writeToParcel(data, 0);
+        } else {
+            data.writeInt(0);
+        }
+        data.writeInt(userId);
+        mRemote.transact(START_VOICE_ACTIVITY_TRANSACTION, data, reply, 0);
+        reply.readException();
+        int result = reply.readInt();
+        reply.recycle();
+        data.recycle();
+        return result;
+    }
     public boolean startNextMatchingActivity(IBinder callingActivity,
             Intent intent, Bundle options) throws RemoteException {
         Parcel data = Parcel.obtain();
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 4562d6e..7dc21b4 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -94,6 +94,7 @@
 import android.renderscript.RenderScript;
 import android.security.AndroidKeyStoreProvider;
 
+import com.android.internal.app.IVoiceInteractor;
 import com.android.internal.os.BinderInternal;
 import com.android.internal.os.RuntimeInit;
 import com.android.internal.os.SamplingProfilerIntegration;
@@ -265,6 +266,7 @@
         IBinder token;
         int ident;
         Intent intent;
+        IVoiceInteractor voiceInteractor;
         Bundle state;
         Activity activity;
         Window window;
@@ -603,6 +605,7 @@
         // activity itself back to the activity manager. (matters more with ipc)
         public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
                 ActivityInfo info, Configuration curConfig, CompatibilityInfo compatInfo,
+                IVoiceInteractor voiceInteractor,
                 int procState, Bundle state, List<ResultInfo> pendingResults,
                 List<Intent> pendingNewIntents, boolean notResumed, boolean isForward,
                 String profileName, ParcelFileDescriptor profileFd, boolean autoStopProfiler,
@@ -615,6 +618,7 @@
             r.token = token;
             r.ident = ident;
             r.intent = intent;
+            r.voiceInteractor = voiceInteractor;
             r.activityInfo = info;
             r.compatInfo = compatInfo;
             r.state = state;
@@ -2197,7 +2201,8 @@
                         + r.activityInfo.name + " with config " + config);
                 activity.attach(appContext, this, getInstrumentation(), r.token,
                         r.ident, app, r.intent, r.activityInfo, title, r.parent,
-                        r.embeddedID, r.lastNonConfigurationInstances, config, options);
+                        r.embeddedID, r.lastNonConfigurationInstances, config, options,
+                        r.voiceInteractor);
 
                 if (customIntent != null) {
                     activity.mIntent = customIntent;
diff --git a/core/java/android/app/ActivityView.java b/core/java/android/app/ActivityView.java
index edf21dd..a810134 100644
--- a/core/java/android/app/ActivityView.java
+++ b/core/java/android/app/ActivityView.java
@@ -147,6 +147,7 @@
         if (mSurface != null) {
             mActivityContainer.startActivity(intent);
         } else {
+            mActivityContainer.checkEmbeddedAllowed(intent);
             mQueuedIntent = intent;
             mQueuedPendingIntent = null;
         }
@@ -162,6 +163,7 @@
         if (mSurface != null) {
             mActivityContainer.startActivityIntentSender(iIntentSender);
         } else {
+            mActivityContainer.checkEmbeddedAllowedIntentSender(iIntentSender);
             mQueuedPendingIntent = iIntentSender;
             mQueuedIntent = null;
         }
@@ -177,6 +179,7 @@
         if (mSurface != null) {
             mActivityContainer.startActivityIntentSender(iIntentSender);
         } else {
+            mActivityContainer.checkEmbeddedAllowedIntentSender(iIntentSender);
             mQueuedPendingIntent = iIntentSender;
             mQueuedIntent = null;
         }
@@ -326,6 +329,24 @@
             }
         }
 
+        void checkEmbeddedAllowed(Intent intent) {
+            try {
+                mIActivityContainer.checkEmbeddedAllowed(intent);
+            } catch (RemoteException e) {
+                throw new RuntimeException(
+                        "ActivityView: Unable to startActivity from Intent. " + e);
+            }
+        }
+
+        void checkEmbeddedAllowedIntentSender(IIntentSender intentSender) {
+            try {
+                mIActivityContainer.checkEmbeddedAllowedIntentSender(intentSender);
+            } catch (RemoteException e) {
+                throw new RuntimeException(
+                        "ActivityView: Unable to startActivity from IntentSender. " + e);
+            }
+        }
+
         int getDisplayId() {
             try {
                 return mIActivityContainer.getDisplayId();
diff --git a/core/java/android/app/ApplicationThreadNative.java b/core/java/android/app/ApplicationThreadNative.java
index f1c632e..fcc7f8e 100644
--- a/core/java/android/app/ApplicationThreadNative.java
+++ b/core/java/android/app/ApplicationThreadNative.java
@@ -33,6 +33,7 @@
 import android.os.IBinder;
 import android.os.Parcel;
 import android.os.ParcelFileDescriptor;
+import com.android.internal.app.IVoiceInteractor;
 
 import java.io.FileDescriptor;
 import java.io.IOException;
@@ -136,6 +137,8 @@
             ActivityInfo info = ActivityInfo.CREATOR.createFromParcel(data);
             Configuration curConfig = Configuration.CREATOR.createFromParcel(data);
             CompatibilityInfo compatInfo = CompatibilityInfo.CREATOR.createFromParcel(data);
+            IVoiceInteractor voiceInteractor = IVoiceInteractor.Stub.asInterface(
+                    data.readStrongBinder());
             int procState = data.readInt();
             Bundle state = data.readBundle();
             List<ResultInfo> ri = data.createTypedArrayList(ResultInfo.CREATOR);
@@ -147,7 +150,8 @@
                     ? ParcelFileDescriptor.CREATOR.createFromParcel(data) : null;
             boolean autoStopProfiler = data.readInt() != 0;
             Bundle resumeArgs = data.readBundle();
-            scheduleLaunchActivity(intent, b, ident, info, curConfig, compatInfo, procState, state,
+            scheduleLaunchActivity(intent, b, ident, info, curConfig, compatInfo,
+                    voiceInteractor, procState, state,
                     ri, pi, notResumed, isForward, profileName, profileFd, autoStopProfiler,
                     resumeArgs);
             return true;
@@ -735,6 +739,7 @@
 
     public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
             ActivityInfo info, Configuration curConfig, CompatibilityInfo compatInfo,
+            IVoiceInteractor voiceInteractor,
             int procState, Bundle state, List<ResultInfo> pendingResults,
             List<Intent> pendingNewIntents, boolean notResumed, boolean isForward,
             String profileName, ParcelFileDescriptor profileFd, boolean autoStopProfiler,
@@ -748,6 +753,7 @@
         info.writeToParcel(data, 0);
         curConfig.writeToParcel(data, 0);
         compatInfo.writeToParcel(data, 0);
+        data.writeStrongBinder(voiceInteractor != null ? voiceInteractor.asBinder() : null);
         data.writeInt(procState);
         data.writeBundle(state);
         data.writeTypedList(pendingResults);
diff --git a/core/java/android/app/IActivityContainer.aidl b/core/java/android/app/IActivityContainer.aidl
index cc3b10c..52884f7 100644
--- a/core/java/android/app/IActivityContainer.aidl
+++ b/core/java/android/app/IActivityContainer.aidl
@@ -29,6 +29,8 @@
     void setSurface(in Surface surface, int width, int height, int density);
     int startActivity(in Intent intent);
     int startActivityIntentSender(in IIntentSender intentSender);
+    void checkEmbeddedAllowed(in Intent intent);
+    void checkEmbeddedAllowedIntentSender(in IIntentSender intentSender);
     int getDisplayId();
     boolean injectEvent(in InputEvent event);
     void release();
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 52003f1..6b94c4e 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -47,6 +47,8 @@
 import android.os.Parcelable;
 import android.os.RemoteException;
 import android.os.StrictMode;
+import android.service.voice.IVoiceInteractionSession;
+import com.android.internal.app.IVoiceInteractor;
 
 import java.util.List;
 
@@ -77,6 +79,10 @@
             IntentSender intent, Intent fillInIntent, String resolvedType,
             IBinder resultTo, String resultWho, int requestCode,
             int flagsMask, int flagsValues, Bundle options) throws RemoteException;
+    public int startVoiceActivity(String callingPackage, int callingPid, int callingUid,
+            Intent intent, String resolvedType, IVoiceInteractionSession session,
+            IVoiceInteractor interactor, int flags, String profileFile,
+            ParcelFileDescriptor profileFd, Bundle options, int userId) throws RemoteException;
     public boolean startNextMatchingActivity(IBinder callingActivity,
             Intent intent, Bundle options) throws RemoteException;
     public boolean finishActivity(IBinder token, int code, Intent data, boolean finishTask)
@@ -733,4 +739,5 @@
     int STOP_LOCK_TASK_MODE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+215;
     int IS_IN_LOCK_TASK_MODE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+216;
     int SET_ACTIVITY_LABEL_ICON_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+217;
+    int START_VOICE_ACTIVITY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+218;
 }
diff --git a/core/java/android/app/IApplicationThread.java b/core/java/android/app/IApplicationThread.java
index ac8ac8f..f290e94 100644
--- a/core/java/android/app/IApplicationThread.java
+++ b/core/java/android/app/IApplicationThread.java
@@ -31,6 +31,8 @@
 import android.os.RemoteException;
 import android.os.IBinder;
 import android.os.IInterface;
+import android.service.voice.IVoiceInteractionSession;
+import com.android.internal.app.IVoiceInteractor;
 
 import java.io.FileDescriptor;
 import java.util.List;
@@ -55,8 +57,9 @@
     void scheduleSendResult(IBinder token, List<ResultInfo> results) throws RemoteException;
     void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
             ActivityInfo info, Configuration curConfig, CompatibilityInfo compatInfo,
-            int procState, Bundle state, List<ResultInfo> pendingResults,
-            List<Intent> pendingNewIntents, boolean notResumed, boolean isForward,
+            IVoiceInteractor voiceInteractor, int procState, Bundle state,
+            List<ResultInfo> pendingResults, List<Intent> pendingNewIntents, boolean notResumed,
+            boolean isForward,
             String profileName, ParcelFileDescriptor profileFd, boolean autoStopProfiler,
             Bundle resumeArgs)
             throws RemoteException;
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index 8681f5c..ad4027d 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -18,11 +18,15 @@
 package android.app;
 
 import android.app.ITransientNotification;
-import android.service.notification.StatusBarNotification;
 import android.app.Notification;
 import android.content.ComponentName;
 import android.content.Intent;
+import android.net.Uri;
+import android.service.notification.Condition;
+import android.service.notification.IConditionListener;
+import android.service.notification.IConditionProvider;
 import android.service.notification.INotificationListener;
+import android.service.notification.StatusBarNotification;
 import android.service.notification.ZenModeConfig;
 
 /** {@hide} */
@@ -53,4 +57,7 @@
 
     ZenModeConfig getZenModeConfig();
     boolean setZenModeConfig(in ZenModeConfig config);
+    oneway void notifyConditions(String pkg, in IConditionProvider provider, in Condition[] conditions);
+    oneway void requestZenModeConditions(in IConditionListener callback, boolean requested);
+    oneway void setZenModeCondition(in Uri conditionId);
 }
\ No newline at end of file
diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java
index 028fa68..e58ccb8 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -1428,7 +1428,7 @@
     }
 
     /**
-     * Like {@link #execStartActivity(Context, IBinder, IBinder, Activity, Intent, int)},
+     * Like {@link #execStartActivity},
      * but accepts an array of activities to be started.  Note that active
      * {@link ActivityMonitor} objects only match against the first activity in
      * the array.
@@ -1442,7 +1442,7 @@
     }
 
     /**
-     * Like {@link #execStartActivity(Context, IBinder, IBinder, Activity, Intent, int)},
+     * Like {@link #execStartActivity},
      * but accepts an array of activities to be started.  Note that active
      * {@link ActivityMonitor} objects only match against the first activity in
      * the array.
@@ -1545,8 +1545,7 @@
     }
 
     /**
-     * Like {@link #execStartActivity(Context, IBinder, IBinder, Activity, Intent, int)},
-     * but for starting as a particular user.
+     * Like {@link #execStartActivity}, but for starting as a particular user.
      *
      * @param who The Context from which the activity is being started.
      * @param contextThread The main thread of the Context from which the activity
@@ -1616,7 +1615,8 @@
         mUiAutomationConnection = uiAutomationConnection;
     }
 
-    /*package*/ static void checkStartActivityResult(int res, Object intent) {
+    /** @hide */
+    public static void checkStartActivityResult(int res, Object intent) {
         if (res >= ActivityManager.START_SUCCESS) {
             return;
         }
@@ -1640,6 +1640,9 @@
             case ActivityManager.START_NOT_ACTIVITY:
                 throw new IllegalArgumentException(
                         "PendingIntent is not an activity");
+            case ActivityManager.START_NOT_VOICE_COMPATIBLE:
+                throw new SecurityException(
+                        "Starting under voice control not allowed for: " + intent);
             default:
                 throw new AndroidRuntimeException("Unknown error code "
                         + res + " when starting " + intent);
diff --git a/core/java/android/app/VoiceInteractor.java b/core/java/android/app/VoiceInteractor.java
new file mode 100644
index 0000000..6dc48b0
--- /dev/null
+++ b/core/java/android/app/VoiceInteractor.java
@@ -0,0 +1,275 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteException;
+import android.util.ArrayMap;
+import android.util.Log;
+import com.android.internal.app.IVoiceInteractor;
+import com.android.internal.app.IVoiceInteractorCallback;
+import com.android.internal.app.IVoiceInteractorRequest;
+import com.android.internal.os.HandlerCaller;
+import com.android.internal.os.SomeArgs;
+
+import java.util.WeakHashMap;
+
+/**
+ * Interface for an {@link Activity} to interact with the user through voice.
+ */
+public class VoiceInteractor {
+    static final String TAG = "VoiceInteractor";
+    static final boolean DEBUG = true;
+
+    final Context mContext;
+    final Activity mActivity;
+    final IVoiceInteractor mInteractor;
+    final HandlerCaller mHandlerCaller;
+    final HandlerCaller.Callback mHandlerCallerCallback = new HandlerCaller.Callback() {
+        @Override
+        public void executeMessage(Message msg) {
+            SomeArgs args = (SomeArgs)msg.obj;
+            Request request;
+            switch (msg.what) {
+                case MSG_CONFIRMATION_RESULT:
+                    request = pullRequest((IVoiceInteractorRequest)args.arg1, true);
+                    if (DEBUG) Log.d(TAG, "onConfirmResult: req="
+                            + ((IVoiceInteractorRequest)args.arg1).asBinder() + "/" + request
+                            + " confirmed=" + msg.arg1 + " result=" + args.arg2);
+                    if (request != null) {
+                        ((ConfirmationRequest)request).onConfirmationResult(msg.arg1 != 0,
+                                (Bundle) args.arg2);
+                        request.clear();
+                    }
+                    break;
+                case MSG_COMMAND_RESULT:
+                    request = pullRequest((IVoiceInteractorRequest)args.arg1, msg.arg1 != 0);
+                    if (DEBUG) Log.d(TAG, "onCommandResult: req="
+                            + ((IVoiceInteractorRequest)args.arg1).asBinder() + "/" + request
+                            + " result=" + args.arg2);
+                    if (request != null) {
+                        ((CommandRequest)request).onCommandResult((Bundle) args.arg2);
+                        if (msg.arg1 != 0) {
+                            request.clear();
+                        }
+                    }
+                    break;
+                case MSG_CANCEL_RESULT:
+                    request = pullRequest((IVoiceInteractorRequest)args.arg1, true);
+                    if (DEBUG) Log.d(TAG, "onCancelResult: req="
+                            + ((IVoiceInteractorRequest)args.arg1).asBinder() + "/" + request);
+                    if (request != null) {
+                        request.onCancel();
+                        request.clear();
+                    }
+                    break;
+            }
+        }
+    };
+
+    final IVoiceInteractorCallback.Stub mCallback = new IVoiceInteractorCallback.Stub() {
+        @Override
+        public void deliverConfirmationResult(IVoiceInteractorRequest request, boolean confirmed,
+                Bundle result) {
+            mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageIOO(
+                    MSG_CONFIRMATION_RESULT, confirmed ? 1 : 0, request, result));
+        }
+
+        @Override
+        public void deliverCommandResult(IVoiceInteractorRequest request, boolean complete,
+                Bundle result) {
+            mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageIOO(
+                    MSG_COMMAND_RESULT, complete ? 1 : 0, request, result));
+        }
+
+        @Override
+        public void deliverCancel(IVoiceInteractorRequest request) throws RemoteException {
+            mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageO(
+                    MSG_CANCEL_RESULT, request));
+        }
+    };
+
+    final ArrayMap<IBinder, Request> mActiveRequests = new ArrayMap<IBinder, Request>();
+
+    static final int MSG_CONFIRMATION_RESULT = 1;
+    static final int MSG_COMMAND_RESULT = 2;
+    static final int MSG_CANCEL_RESULT = 3;
+
+    public static abstract class Request {
+        IVoiceInteractorRequest mRequestInterface;
+        Context mContext;
+        Activity mActivity;
+
+        public Request() {
+        }
+
+        public void cancel() {
+            try {
+                mRequestInterface.cancel();
+            } catch (RemoteException e) {
+                Log.w(TAG, "Voice interactor has died", e);
+            }
+        }
+
+        public Context getContext() {
+            return mContext;
+        }
+
+        public Activity getActivity() {
+            return mActivity;
+        }
+
+        public void onCancel() {
+        }
+
+        void clear() {
+            mRequestInterface = null;
+            mContext = null;
+            mActivity = null;
+        }
+
+        abstract IVoiceInteractorRequest submit(IVoiceInteractor interactor,
+                String packageName, IVoiceInteractorCallback callback) throws RemoteException;
+    }
+
+    public static class ConfirmationRequest extends Request {
+        final CharSequence mPrompt;
+        final Bundle mExtras;
+
+        /**
+         * Confirms an operation with the user via the trusted system
+         * VoiceInteractionService.  This allows an Activity to complete an unsafe operation that
+         * would require the user to touch the screen when voice interaction mode is not enabled.
+         * The result of the confirmation will be returned through an asynchronous call to
+         * either {@link #onConfirmationResult(boolean, android.os.Bundle)} or
+         * {@link #onCancel()}.
+         *
+         * <p>In some cases this may be a simple yes / no confirmation or the confirmation could
+         * include context information about how the action will be completed
+         * (e.g. booking a cab might include details about how long until the cab arrives)
+         * so the user can give a confirmation.
+         * @param prompt Optional confirmation text to read to the user as the action being
+         * confirmed.
+         * @param extras Additional optional information.
+         */
+        public ConfirmationRequest(CharSequence prompt, Bundle extras) {
+            mPrompt = prompt;
+            mExtras = extras;
+        }
+
+        public void onConfirmationResult(boolean confirmed, Bundle result) {
+        }
+
+        IVoiceInteractorRequest submit(IVoiceInteractor interactor, String packageName,
+                IVoiceInteractorCallback callback) throws RemoteException {
+            return interactor.startConfirmation(packageName, callback, mPrompt.toString(), mExtras);
+        }
+   }
+
+    public static class CommandRequest extends Request {
+        final String mCommand;
+        final Bundle mArgs;
+
+        /**
+         * Execute a command using the trusted system VoiceInteractionService.
+         * This allows an Activity to request additional information from the user needed to
+         * complete an action (e.g. booking a table might have several possible times that the
+         * user could select from or an app might need the user to agree to a terms of service).
+         * The result of the confirmation will be returned through an asynchronous call to
+         * either {@link #onCommandResult(android.os.Bundle)} or
+         * {@link #onCancel()}.
+         *
+         * <p>The command is a string that describes the generic operation to be performed.
+         * The command will determine how the properties in extras are interpreted and the set of
+         * available commands is expected to grow over time.  An example might be
+         * "com.google.voice.commands.REQUEST_NUMBER_BAGS" to request the number of bags as part of
+         * airline check-in.  (This is not an actual working example.)
+         *
+         * @param command The desired command to perform.
+         * @param args Additional arguments to control execution of the command.
+         */
+        public CommandRequest(String command, Bundle args) {
+            mCommand = command;
+            mArgs = args;
+        }
+
+        public void onCommandResult(Bundle result) {
+        }
+
+        IVoiceInteractorRequest submit(IVoiceInteractor interactor, String packageName,
+                IVoiceInteractorCallback callback) throws RemoteException {
+            return interactor.startConfirmation(packageName, callback, mCommand, mArgs);
+        }
+   }
+
+    VoiceInteractor(Context context, Activity activity, IVoiceInteractor interactor,
+            Looper looper) {
+        mContext = context;
+        mActivity = activity;
+        mInteractor = interactor;
+        mHandlerCaller = new HandlerCaller(context, looper, mHandlerCallerCallback, true);
+    }
+
+    Request pullRequest(IVoiceInteractorRequest request, boolean complete) {
+        synchronized (mActiveRequests) {
+            Request req = mActiveRequests.get(request.asBinder());
+            if (req != null && complete) {
+                mActiveRequests.remove(request.asBinder());
+            }
+            return req;
+        }
+    }
+
+    public boolean submitRequest(Request request) {
+        try {
+            IVoiceInteractorRequest ireq = request.submit(mInteractor,
+                    mContext.getOpPackageName(), mCallback);
+            request.mRequestInterface = ireq;
+            request.mContext = mContext;
+            request.mActivity = mActivity;
+            synchronized (mActiveRequests) {
+                mActiveRequests.put(ireq.asBinder(), request);
+            }
+            return true;
+        } catch (RemoteException e) {
+            Log.w(TAG, "Remove voice interactor service died", e);
+            return false;
+        }
+    }
+
+    /**
+     * Queries the supported commands available from the VoiceinteractionService.
+     * The command is a string that describes the generic operation to be performed.
+     * An example might be "com.google.voice.commands.REQUEST_NUMBER_BAGS" to request the number
+     * of bags as part of airline check-in.  (This is not an actual working example.)
+     *
+     * @param commands
+     */
+    public boolean[] supportsCommands(String[] commands) {
+        try {
+            boolean[] res = mInteractor.supportsCommands(mContext.getOpPackageName(), commands);
+            if (DEBUG) Log.d(TAG, "supportsCommands: cmds=" + commands + " res=" + res);
+            return res;
+        } catch (RemoteException e) {
+            throw new RuntimeException("Voice interactor has died", e);
+        }
+    }
+}
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index e6e0f35..f08097b 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -16,6 +16,7 @@
 
 package android.app;
 
+import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
@@ -44,10 +45,14 @@
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.os.SystemProperties;
+import android.text.TextUtils;
 import android.util.Log;
 import android.view.WindowManagerGlobal;
 
 import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
@@ -65,6 +70,11 @@
     private float mWallpaperXStep = -1;
     private float mWallpaperYStep = -1;
 
+    /** {@hide} */
+    private static final String PROP_WALLPAPER = "ro.config.wallpaper";
+    /** {@hide} */
+    private static final String PROP_WALLPAPER_COMPONENT = "ro.config.wallpaper_component";
+
     /**
      * Activity Action: Show settings for choosing wallpaper. Do not use directly to construct
      * an intent; instead, use {@link #getCropAndSetWallpaperIntent}.
@@ -298,8 +308,7 @@
         
         private Bitmap getDefaultWallpaperLocked(Context context) {
             try {
-                InputStream is = context.getResources().openRawResource(
-                        com.android.internal.R.drawable.default_wallpaper);
+                InputStream is = openDefaultWallpaper(context);
                 if (is != null) {
                     int width = mService.getWidthHint();
                     int height = mService.getHeightHint();
@@ -403,8 +412,7 @@
         horizontalAlignment = Math.max(0, Math.min(1, horizontalAlignment));
         verticalAlignment = Math.max(0, Math.min(1, verticalAlignment));
 
-        InputStream is = new BufferedInputStream(
-                resources.openRawResource(com.android.internal.R.drawable.default_wallpaper));
+        InputStream is = new BufferedInputStream(openDefaultWallpaper(mContext));
 
         if (is == null) {
             Log.e(TAG, "default wallpaper input stream is null");
@@ -429,8 +437,7 @@
                     }
                 }
 
-                is = new BufferedInputStream(resources.openRawResource(
-                        com.android.internal.R.drawable.default_wallpaper));
+                is = new BufferedInputStream(openDefaultWallpaper(mContext));
 
                 RectF cropRectF;
 
@@ -479,8 +486,7 @@
 
                 if (crop == null) {
                     // BitmapRegionDecoder has failed, try to crop in-memory
-                    is = new BufferedInputStream(resources.openRawResource(
-                            com.android.internal.R.drawable.default_wallpaper));
+                    is = new BufferedInputStream(openDefaultWallpaper(mContext));
                     Bitmap fullSize = null;
                     if (is != null) {
                         BitmapFactory.Options options = new BitmapFactory.Options();
@@ -1009,6 +1015,53 @@
      * wallpaper.
      */
     public void clear() throws IOException {
-        setResource(com.android.internal.R.drawable.default_wallpaper);
+        setStream(openDefaultWallpaper(mContext));
+    }
+
+    /**
+     * Open stream representing the default static image wallpaper.
+     *
+     * @hide
+     */
+    public static InputStream openDefaultWallpaper(Context context) {
+        final String path = SystemProperties.get(PROP_WALLPAPER);
+        if (!TextUtils.isEmpty(path)) {
+            final File file = new File(path);
+            if (file.exists()) {
+                try {
+                    return new FileInputStream(file);
+                } catch (IOException e) {
+                    // Ignored, fall back to platform default below
+                }
+            }
+        }
+        return context.getResources().openRawResource(
+                com.android.internal.R.drawable.default_wallpaper);
+    }
+
+    /**
+     * Return {@link ComponentName} of the default live wallpaper, or
+     * {@code null} if none is defined.
+     *
+     * @hide
+     */
+    public static ComponentName getDefaultWallpaperComponent(Context context) {
+        String flat = SystemProperties.get(PROP_WALLPAPER_COMPONENT);
+        if (!TextUtils.isEmpty(flat)) {
+            final ComponentName cn = ComponentName.unflattenFromString(flat);
+            if (cn != null) {
+                return cn;
+            }
+        }
+
+        flat = context.getString(com.android.internal.R.string.default_wallpaper_component);
+        if (!TextUtils.isEmpty(flat)) {
+            final ComponentName cn = ComponentName.unflattenFromString(flat);
+            if (cn != null) {
+                return cn;
+            }
+        }
+
+        return null;
     }
 }
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index d7170e8..68ab611 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -33,6 +33,7 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.UserHandle;
+import android.service.trust.TrustAgentService;
 import android.util.Log;
 
 import com.android.org.conscrypt.TrustedCertificateStore;
@@ -1267,7 +1268,7 @@
     public static final int KEYGUARD_DISABLE_FEATURES_NONE = 0;
 
     /**
-     * Disable all keyguard widgets
+     * Disable all keyguard widgets. Has no effect.
      */
     public static final int KEYGUARD_DISABLE_WIDGETS_ALL = 1 << 0;
 
@@ -1277,6 +1278,22 @@
     public static final int KEYGUARD_DISABLE_SECURE_CAMERA = 1 << 1;
 
     /**
+     * Disable showing all notifications on secure keyguard screens (e.g. PIN/Pattern/Password)
+     */
+    public static final int KEYGUARD_DISABLE_SECURE_NOTIFICATIONS = 1 << 2;
+
+    /**
+     * Only allow redacted notifications on secure keyguard screens (e.g. PIN/Pattern/Password)
+     */
+    public static final int KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS = 1 << 3;
+
+    /**
+     * Ignore {@link TrustAgentService} state on secure keyguard screens
+     * (e.g. PIN/Pattern/Password).
+     */
+    public static final int KEYGUARD_DISABLE_TRUST_AGENTS = 1 << 4;
+
+    /**
      * Disable all current and future keyguard customizations.
      */
     public static final int KEYGUARD_DISABLE_FEATURES_ALL = 0x7fffffff;
@@ -1496,7 +1513,8 @@
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
      * @param which {@link #KEYGUARD_DISABLE_FEATURES_NONE} (default),
      * {@link #KEYGUARD_DISABLE_WIDGETS_ALL}, {@link #KEYGUARD_DISABLE_SECURE_CAMERA},
-     * {@link #KEYGUARD_DISABLE_FEATURES_ALL}
+     * {@link #KEYGUARD_DISABLE_SECURE_NOTIFICATIONS}, {@link #KEYGUARD_DISABLE_TRUST_AGENTS},
+     * {@link #KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS}, {@link #KEYGUARD_DISABLE_FEATURES_ALL}
      */
     public void setKeyguardDisabledFeatures(ComponentName admin, int which) {
         if (mService != null) {
@@ -1957,4 +1975,48 @@
         }
         return null;
     }
+
+    /**
+     * Called by a profile or device owner to set a user restriction specified
+     * by the key.
+     * <p>
+     * The calling device admin must be a profile or device owner; if it is not,
+     * a security exception will be thrown.
+     *
+     * @param admin Which {@link DeviceAdminReceiver} this request is associated
+     *            with.
+     * @param key The key of the restriction. See the constants in
+     *            {@link android.os.UserManager} for the list of keys.
+     */
+    public void addUserRestriction(ComponentName admin, String key) {
+        if (mService != null) {
+            try {
+                mService.setUserRestriction(admin, key, true);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed talking with device policy service", e);
+            }
+        }
+    }
+
+    /**
+     * Called by a profile or device owner to clear a user restriction specified
+     * by the key.
+     * <p>
+     * The calling device admin must be a profile or device owner; if it is not,
+     * a security exception will be thrown.
+     *
+     * @param admin Which {@link DeviceAdminReceiver} this request is associated
+     *            with.
+     * @param key The key of the restriction. See the constants in
+     *            {@link android.os.UserManager} for the list of keys.
+     */
+    public void clearUserRestriction(ComponentName admin, String key) {
+        if (mService != null) {
+            try {
+                mService.setUserRestriction(admin, key, false);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed talking with device policy service", e);
+            }
+        }
+    }
 }
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 85ba58b..72b3c20 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -119,4 +119,6 @@
 
     void setApplicationRestrictions(in ComponentName who, in String packageName, in Bundle settings);
     Bundle getApplicationRestrictions(in ComponentName who, in String packageName);
+
+    void setUserRestriction(in ComponentName who, in String key, boolean enable);
 }
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index cbb6cf5..de223a3 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -2442,6 +2442,14 @@
     public static final String APPWIDGET_SERVICE = "appwidget";
 
     /**
+     * Official published name of the (internal) voice interaction manager service.
+     *
+     * @hide
+     * @see #getSystemService
+     */
+    public static final String VOICE_INTERACTION_MANAGER_SERVICE = "voiceinteraction";
+
+    /**
      * Use with {@link #getSystemService} to retrieve an
      * {@link android.app.backup.IBackupManager IBackupManager} for communicating
      * with the backup mechanism.
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index c0f04af..ae5437b 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -2804,6 +2804,14 @@
     @SdkConstant(SdkConstantType.INTENT_CATEGORY)
     public static final String CATEGORY_BROWSABLE = "android.intent.category.BROWSABLE";
     /**
+     * Categories for activities that can participate in voice interaction.
+     * An activity that supports this category must be prepared to run with
+     * no UI shown at all (though in some case it may have a UI shown), and
+     * rely on {@link android.app.VoiceInteractor} to interact with the user.
+     */
+    @SdkConstant(SdkConstantType.INTENT_CATEGORY)
+    public static final String CATEGORY_VOICE = "android.intent.category.VOICE";
+    /**
      * Set if the activity should be considered as an alternative action to
      * the data the user is currently viewing.  See also
      * {@link #CATEGORY_SELECTED_ALTERNATIVE} for an alternative action that
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index ae0899f..488e25f 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -74,6 +74,9 @@
 
     ActivityInfo getActivityInfo(in ComponentName className, int flags, int userId);
 
+    boolean activitySupportsIntent(in ComponentName className, in Intent intent,
+            String resolvedType);
+
     ActivityInfo getReceiverInfo(in ComponentName className, int flags, int userId);
 
     ServiceInfo getServiceInfo(in ComponentName className, int flags, int userId);
diff --git a/core/java/android/content/pm/LauncherActivityInfo.java b/core/java/android/content/pm/LauncherActivityInfo.java
index 92b9146..9087338 100644
--- a/core/java/android/content/pm/LauncherActivityInfo.java
+++ b/core/java/android/content/pm/LauncherActivityInfo.java
@@ -19,6 +19,8 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Bitmap.Config;
@@ -37,6 +39,8 @@
  */
 public class LauncherActivityInfo {
     private static final boolean DEBUG = false;
+    private static final String TAG = "LauncherActivityInfo";
+
     private final PackageManager mPm;
     private final UserManager mUm;
 
@@ -121,25 +125,42 @@
     }
 
     /**
+     * Returns the name for the acitivty from  android:name in the manifest.
+     * @return the name from android:name for the acitivity.
+     */
+    public String getName() {
+        return mActivityInfo.name;
+    }
+
+    /**
      * Returns the activity icon with badging appropriate for the profile.
      * @param density Optional density for the icon, or 0 to use the default density.
      * @return A badged icon for the activity.
      */
     public Drawable getBadgedIcon(int density) {
-        // TODO: Handle density
-        if (mUser.equals(android.os.Process.myUserHandle())) {
-            return mActivityInfo.loadIcon(mPm);
-        }
-        Drawable originalIcon = mActivityInfo.loadIcon(mPm);
-        if (originalIcon == null) {
-            if (DEBUG) {
-                Log.w("LauncherActivityInfo", "Couldn't find icon for activity");
+        int iconRes = mActivityInfo.getIconResource();
+        Resources resources = null;
+        Drawable originalIcon = null;
+        try {
+            resources = mPm.getResourcesForApplication(mActivityInfo.applicationInfo);
+            try {
+                if (density != 0) {
+                    originalIcon = resources.getDrawableForDensity(iconRes, density);
+                }
+            } catch (Resources.NotFoundException e) {
             }
-            originalIcon = mPm.getDefaultActivityIcon();
+        } catch (NameNotFoundException nnfe) {
         }
+
+        if (originalIcon == null) {
+            originalIcon = mActivityInfo.loadIcon(mPm);
+        }
+
         if (originalIcon instanceof BitmapDrawable) {
             return mUm.getBadgedDrawableForUser(
                     originalIcon, mUser);
+        } else {
+            Log.e(TAG, "Unable to create badged icon for " + mActivityInfo);
         }
         return originalIcon;
     }
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index d981cc1..484a2a1 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -1235,7 +1235,6 @@
      */
     @SdkConstant(SdkConstantType.FEATURE)
     public static final String FEATURE_LIVE_WALLPAPER = "android.software.live_wallpaper";
-
     /**
      * Feature for {@link #getSystemAvailableFeatures} and
      * {@link #hasSystemFeature}: The device supports app widgets.
@@ -1244,6 +1243,17 @@
     public static final String FEATURE_APP_WIDGETS = "android.software.app_widgets";
 
     /**
+     * @hide
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device supports
+     * {@link android.service.voice.VoiceInteractionService} and
+     * {@link android.app.VoiceInteractor}.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_VOICE_RECOGNIZERS = "android.software.voice_recognizers";
+
+
+    /**
      * Feature for {@link #getSystemAvailableFeatures} and
      * {@link #hasSystemFeature}: The device supports a home screen that is replaceable
      * by third party applications.
diff --git a/core/java/android/hardware/location/GeofenceHardware.java b/core/java/android/hardware/location/GeofenceHardware.java
index 21de9f5..4c074e9 100644
--- a/core/java/android/hardware/location/GeofenceHardware.java
+++ b/core/java/android/hardware/location/GeofenceHardware.java
@@ -79,7 +79,7 @@
      */
     public static final int MONITOR_UNSUPPORTED = 2;
 
-    // The following constants need to match geofence flags in gps.h
+    // The following constants need to match geofence flags in gps.h and fused_location.h
     /**
      * The constant to indicate that the user has entered the geofence.
      */
@@ -92,7 +92,7 @@
 
     /**
      * The constant to indicate that the user is uncertain with respect to a
-     * geofence.                                                  nn
+     * geofence.
      */
     public static final int GEOFENCE_UNCERTAIN = 1<<2L;
 
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 11e3795..0336dd6 100644
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -16,12 +16,17 @@
 
 package android.os;
 
+import android.text.TextUtils;
+import android.util.Slog;
+
 import com.android.internal.telephony.TelephonyProperties;
 
 /**
  * Information about the current build, extracted from system properties.
  */
 public class Build {
+    private static final String TAG = "Build";
+
     /** Value used for when a build property is unknown. */
     public static final String UNKNOWN = "unknown";
 
@@ -509,7 +514,43 @@
     public static final String TAGS = getString("ro.build.tags");
 
     /** A string that uniquely identifies this build.  Do not attempt to parse this value. */
-    public static final String FINGERPRINT = getString("ro.build.fingerprint");
+    public static final String FINGERPRINT = deriveFingerprint();
+
+    /**
+     * Some devices split the fingerprint components between multiple
+     * partitions, so we might derive the fingerprint at runtime.
+     */
+    private static String deriveFingerprint() {
+        String finger = SystemProperties.get("ro.build.fingerprint");
+        if (TextUtils.isEmpty(finger)) {
+            finger = getString("ro.product.brand") + '/' +
+                    getString("ro.product.name") + '/' +
+                    getString("ro.product.device") + ':' +
+                    getString("ro.build.version.release") + '/' +
+                    getString("ro.build.id") + '/' +
+                    getString("ro.build.version.incremental") + ':' +
+                    getString("ro.build.type") + '/' +
+                    getString("ro.build.tags");
+        }
+        return finger;
+    }
+
+    /**
+     * Ensure that raw fingerprint system property is defined. If it was derived
+     * dynamically by {@link #deriveFingerprint()} this is where we push the
+     * derived value into the property service.
+     *
+     * @hide
+     */
+    public static void ensureFingerprintProperty() {
+        if (TextUtils.isEmpty(SystemProperties.get("ro.build.fingerprint"))) {
+            try {
+                SystemProperties.set("ro.build.fingerprint", FINGERPRINT);
+            } catch (IllegalArgumentException e) {
+                Slog.e(TAG, "Failed to set fingerprint property", e);
+            }
+        }
+    }
 
     // The following properties only make sense for internal engineering builds.
     public static final long TIME = getLong("ro.build.date.utc") * 1000;
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 63de9a0..1fe9337 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -300,8 +300,7 @@
 
     /**
      * Sets all the user-wide restrictions for this user.
-     * Requires the MANAGE_USERS permission or profile/device owner
-     * privileges.
+     * Requires the MANAGE_USERS permission.
      * @param restrictions the Bundle containing all the restrictions.
      */
     public void setUserRestrictions(Bundle restrictions) {
@@ -310,8 +309,7 @@
 
     /**
      * Sets all the user-wide restrictions for the specified user.
-     * Requires the MANAGE_USERS permission or profile/device owner
-     * privileges.
+     * Requires the MANAGE_USERS permission.
      * @param restrictions the Bundle containing all the restrictions.
      * @param userHandle the UserHandle of the user for whom to set the restrictions.
      */
@@ -325,8 +323,7 @@
 
     /**
      * Sets the value of a specific restriction.
-     * Requires the MANAGE_USERS permission or profile/device owner
-     * privileges.
+     * Requires the MANAGE_USERS permission.
      * @param key the key of the restriction
      * @param value the value for the restriction
      */
@@ -339,8 +336,7 @@
     /**
      * @hide
      * Sets the value of a specific restriction on a specific user.
-     * Requires the {@link android.Manifest.permission#MANAGE_USERS} permission or profile/device owner
-     * privileges.
+     * Requires the MANAGE_USERS permission.
      * @param key the key of the restriction
      * @param value the value for the restriction
      * @param userHandle the user whose restriction is to be changed.
@@ -492,7 +488,8 @@
         ArrayList<UserHandle> profiles = new ArrayList<UserHandle>();
         List<UserInfo> users = new ArrayList<UserInfo>();
         try {
-            users = mService.getProfiles(UserHandle.myUserId(), true /* enabledOnly */);
+            // TODO: Switch enabledOnly to true once client apps are updated
+            users = mService.getProfiles(UserHandle.myUserId(), false /* enabledOnly */);
         } catch (RemoteException re) {
             Log.w(TAG, "Could not get user list", re);
             return null;
@@ -560,7 +557,7 @@
     /**
      * Returns information for all users on this device. Requires
      * {@link android.Manifest.permission#MANAGE_USERS} permission.
-     *
+     * 
      * @param excludeDying specify if the list should exclude users being
      *            removed.
      * @return the list of users that were created.
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 7f9f862..ab06230 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -723,6 +723,13 @@
             = "android.settings.NOTIFICATION_LISTENER_SETTINGS";
 
     /**
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_CONDITION_PROVIDER_SETTINGS
+            = "android.settings.ACTION_CONDITION_PROVIDER_SETTINGS";
+
+    /**
      * Activity Action: Show settings for video captioning.
      * <p>
      * In some cases, a matching Activity may not exist, so ensure you safeguard
@@ -757,6 +764,11 @@
     @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
     public static final String ACTION_ZEN_MODE_SETTINGS = "android.settings.ZEN_MODE_SETTINGS";
 
+    /** {@hide} */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String
+            ACTION_SHOW_REGULATORY_INFO = "android.settings.SHOW_REGULATORY_INFO";
+
     // End of Intent actions for Settings
 
     /**
@@ -3311,6 +3323,12 @@
                 "input_method_selector_visibility";
 
         /**
+         * The currently selected voice interaction service flattened ComponentName.
+         * @hide
+         */
+        public static final String VOICE_INTERACTION_SERVICE = "voice_interaction_service";
+
+        /**
          * bluetooth HCI snoop log configuration
          * @hide
          */
@@ -4510,6 +4528,11 @@
          */
         public static final String ENABLED_NOTIFICATION_LISTENERS = "enabled_notification_listeners";
 
+        /**
+         * @hide
+         */
+        public static final String ENABLED_CONDITION_PROVIDERS = "enabled_condition_providers";
+
         /** @hide */
         public static final String BAR_SERVICE_COMPONENT = "bar_service_component";
 
diff --git a/core/java/android/service/notification/Condition.aidl b/core/java/android/service/notification/Condition.aidl
new file mode 100644
index 0000000..432852c
--- /dev/null
+++ b/core/java/android/service/notification/Condition.aidl
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2014, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.notification;
+
+parcelable Condition;
+
diff --git a/core/java/android/service/notification/Condition.java b/core/java/android/service/notification/Condition.java
new file mode 100644
index 0000000..71e3166
--- /dev/null
+++ b/core/java/android/service/notification/Condition.java
@@ -0,0 +1,147 @@
+/**
+ * Copyright (c) 2014, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.notification;
+
+import android.content.Context;
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * Condition information from condition providers.
+ *
+ * @hide
+ */
+public class Condition implements Parcelable {
+
+    public static final String SCHEME = "condition";
+
+    public static final int STATE_FALSE = 0;
+    public static final int STATE_TRUE = 1;
+    public static final int STATE_UNKNOWN = 2;
+    public static final int STATE_ERROR = 3;
+
+    public static final int FLAG_RELEVANT_NOW = 1 << 0;
+    public static final int FLAG_RELEVANT_ALWAYS = 1 << 1;
+
+    public final Uri id;
+    public String caption;
+    public int state;
+    public int flags;
+
+    public Condition(Uri id, String caption, int state, int flags) {
+        if (id == null) throw new IllegalArgumentException("id is required");
+        if (caption == null) throw new IllegalArgumentException("caption is required");
+        if (!isValidState(state)) throw new IllegalArgumentException("state is invalid: " + state);
+        this.id = id;
+        this.caption = caption;
+        this.state = state;
+        this.flags = flags;
+    }
+
+    private Condition(Parcel source) {
+        this((Uri)source.readParcelable(Condition.class.getClassLoader()),
+                source.readString(),
+                source.readInt(),
+                source.readInt());
+    }
+
+    private static boolean isValidState(int state) {
+        return state >= STATE_FALSE && state <= STATE_ERROR;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeParcelable(id, 0);
+        dest.writeString(caption);
+        dest.writeInt(state);
+        dest.writeInt(flags);
+    }
+
+    @Override
+    public String toString() {
+        return new StringBuilder(Condition.class.getSimpleName()).append('[')
+            .append("id=").append(id)
+            .append(",caption=").append(caption)
+            .append(",state=").append(stateToString(state))
+            .append(",flags=").append(flags)
+            .append(']').toString();
+    }
+
+    public static String stateToString(int state) {
+        if (state == STATE_FALSE) return "STATE_FALSE";
+        if (state == STATE_TRUE) return "STATE_TRUE";
+        if (state == STATE_UNKNOWN) return "STATE_UNKNOWN";
+        if (state == STATE_ERROR) return "STATE_ERROR";
+        throw new IllegalArgumentException("state is invalid: " + state);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (!(o instanceof Condition)) return false;
+        if (o == this) return true;
+        final Condition other = (Condition) o;
+        return Objects.equals(other.id, id)
+                && Objects.equals(other.caption, caption)
+                && other.state == state
+                && other.flags == flags;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(id, caption, state, flags);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    public Condition copy() {
+        final Parcel parcel = Parcel.obtain();
+        try {
+            writeToParcel(parcel, 0);
+            parcel.setDataPosition(0);
+            return new Condition(parcel);
+        } finally {
+            parcel.recycle();
+        }
+    }
+
+    public static Uri.Builder newId(Context context) {
+        return new Uri.Builder().scheme(SCHEME).authority(context.getPackageName());
+    }
+
+    public static boolean isValidId(Uri id, String pkg) {
+        return id != null && id.getScheme().equals(SCHEME) && id.getAuthority().equals(pkg);
+    }
+
+    public static final Parcelable.Creator<Condition> CREATOR
+            = new Parcelable.Creator<Condition>() {
+        @Override
+        public Condition createFromParcel(Parcel source) {
+            return new Condition(source);
+        }
+
+        @Override
+        public Condition[] newArray(int size) {
+            return new Condition[size];
+        }
+    };
+}
diff --git a/core/java/android/service/notification/ConditionProviderService.java b/core/java/android/service/notification/ConditionProviderService.java
new file mode 100644
index 0000000..d6ef8f5
--- /dev/null
+++ b/core/java/android/service/notification/ConditionProviderService.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.notification;
+
+import android.annotation.SdkConstant;
+import android.app.INotificationManager;
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.IBinder;
+import android.os.ServiceManager;
+import android.util.Log;
+
+/**
+ * A service that provides conditions about boolean state.
+ * <p>To extend this class, you must declare the service in your manifest file with
+ * the {@link android.Manifest.permission#BIND_CONDITION_PROVIDER_SERVICE} permission
+ * and include an intent filter with the {@link #SERVICE_INTERFACE} action. For example:</p>
+ * <pre>
+ * &lt;service android:name=".MyConditionProvider"
+ *          android:label="&#64;string/service_name"
+ *          android:permission="android.permission.BIND_CONDITION_PROVIDER_SERVICE">
+ *     &lt;intent-filter>
+ *         &lt;action android:name="android.service.notification.ConditionProviderService" />
+ *     &lt;/intent-filter>
+ * &lt;/service></pre>
+ *
+ * @hide
+ */
+public abstract class ConditionProviderService extends Service {
+    private final String TAG = ConditionProviderService.class.getSimpleName()
+            + "[" + getClass().getSimpleName() + "]";
+
+    private Provider mProvider;
+    private INotificationManager mNoMan;
+
+    /**
+     * The {@link Intent} that must be declared as handled by the service.
+     */
+    @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
+    public static final String SERVICE_INTERFACE
+            = "android.service.notification.ConditionProviderService";
+
+    abstract public void onConnected();
+    abstract public void onRequestConditions(int relevance);
+    abstract public void onSubscribe(Uri conditionId);
+    abstract public void onUnsubscribe(Uri conditionId);
+
+    private final INotificationManager getNotificationInterface() {
+        if (mNoMan == null) {
+            mNoMan = INotificationManager.Stub.asInterface(
+                    ServiceManager.getService(Context.NOTIFICATION_SERVICE));
+        }
+        return mNoMan;
+    }
+
+    public final void notifyCondition(Condition condition) {
+        if (condition == null) return;
+        notifyConditions(new Condition[]{ condition });
+    }
+
+    public final void notifyConditions(Condition... conditions) {
+        if (!isBound() || conditions == null) return;
+        try {
+            getNotificationInterface().notifyConditions(getPackageName(), mProvider, conditions);
+        } catch (android.os.RemoteException ex) {
+            Log.v(TAG, "Unable to contact notification manager", ex);
+        }
+    }
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        if (mProvider == null) {
+            mProvider = new Provider();
+        }
+        return mProvider;
+    }
+
+    private boolean isBound() {
+        if (mProvider == null) {
+            Log.w(TAG, "Condition provider service not yet bound.");
+            return false;
+        }
+        return true;
+    }
+
+    private final class Provider extends IConditionProvider.Stub {
+        private final ConditionProviderService mService = ConditionProviderService.this;
+
+        @Override
+        public void onConnected() {
+            try {
+                mService.onConnected();
+            } catch (Throwable t) {
+                Log.w(TAG, "Error running onConnected", t);
+            }
+        }
+
+        @Override
+        public void onRequestConditions(int relevance) {
+            try {
+                mService.onRequestConditions(relevance);
+            } catch (Throwable t) {
+                Log.w(TAG, "Error running onRequestConditions", t);
+            }
+        }
+
+        @Override
+        public void onSubscribe(Uri conditionId) {
+            try {
+                mService.onSubscribe(conditionId);
+            } catch (Throwable t) {
+                Log.w(TAG, "Error running onSubscribe", t);
+            }
+        }
+
+        @Override
+        public void onUnsubscribe(Uri conditionId) {
+            try {
+                mService.onUnsubscribe(conditionId);
+            } catch (Throwable t) {
+                Log.w(TAG, "Error running onUnsubscribe", t);
+            }
+        }
+    }
+}
diff --git a/core/java/android/service/notification/IConditionListener.aidl b/core/java/android/service/notification/IConditionListener.aidl
new file mode 100644
index 0000000..01f874f
--- /dev/null
+++ b/core/java/android/service/notification/IConditionListener.aidl
@@ -0,0 +1,25 @@
+/**
+ * Copyright (c) 2014, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.notification;
+
+import android.net.Uri;
+import android.service.notification.Condition;
+
+/** @hide */
+oneway interface IConditionListener {
+    void onConditionsReceived(in Condition[] conditions);
+}
\ No newline at end of file
diff --git a/core/java/android/service/notification/IConditionProvider.aidl b/core/java/android/service/notification/IConditionProvider.aidl
new file mode 100644
index 0000000..ada8939
--- /dev/null
+++ b/core/java/android/service/notification/IConditionProvider.aidl
@@ -0,0 +1,28 @@
+/**
+ * Copyright (c) 2014, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.notification;
+
+import android.net.Uri;
+import android.service.notification.Condition;
+
+/** @hide */
+oneway interface IConditionProvider {
+    void onConnected();
+    void onRequestConditions(int relevance);
+    void onSubscribe(in Uri conditionId);
+    void onUnsubscribe(in Uri conditionId);
+}
\ No newline at end of file
diff --git a/core/java/android/service/notification/StatusBarNotification.java b/core/java/android/service/notification/StatusBarNotification.java
index 2c0b76d..72720d1 100644
--- a/core/java/android/service/notification/StatusBarNotification.java
+++ b/core/java/android/service/notification/StatusBarNotification.java
@@ -87,7 +87,7 @@
     }
 
     private String key() {
-        return pkg + '|' + opPkg + '|' + id + '|' + tag + '|' + uid;
+        return pkg + '|' + id + '|' + tag + '|' + uid;
     }
 
     public void writeToParcel(Parcel out, int flags) {
diff --git a/core/java/android/service/voice/IVoiceInteractionService.aidl b/core/java/android/service/voice/IVoiceInteractionService.aidl
new file mode 100644
index 0000000..e9e2f4c
--- /dev/null
+++ b/core/java/android/service/voice/IVoiceInteractionService.aidl
@@ -0,0 +1,23 @@
+/*
+ * 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.voice;
+
+/**
+ * @hide
+ */
+oneway interface IVoiceInteractionService {
+}
diff --git a/core/java/android/service/voice/IVoiceInteractionSession.aidl b/core/java/android/service/voice/IVoiceInteractionSession.aidl
new file mode 100644
index 0000000..7dbf66b
--- /dev/null
+++ b/core/java/android/service/voice/IVoiceInteractionSession.aidl
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.voice;
+
+import android.os.Bundle;
+
+import com.android.internal.app.IVoiceInteractorCallback;
+import com.android.internal.app.IVoiceInteractorRequest;
+
+/**
+ * @hide
+ */
+interface IVoiceInteractionSession {
+}
diff --git a/core/java/android/service/voice/IVoiceInteractionSessionService.aidl b/core/java/android/service/voice/IVoiceInteractionSessionService.aidl
new file mode 100644
index 0000000..2519442
--- /dev/null
+++ b/core/java/android/service/voice/IVoiceInteractionSessionService.aidl
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.voice;
+
+import android.os.Bundle;
+
+import android.service.voice.IVoiceInteractionSession;
+
+/**
+ * @hide
+ */
+oneway interface IVoiceInteractionSessionService {
+    void newSession(IBinder token, in Bundle args);
+}
diff --git a/core/java/android/service/voice/VoiceInteractionService.java b/core/java/android/service/voice/VoiceInteractionService.java
new file mode 100644
index 0000000..d005890
--- /dev/null
+++ b/core/java/android/service/voice/VoiceInteractionService.java
@@ -0,0 +1,77 @@
+/**
+ * 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.voice;
+
+import android.annotation.SdkConstant;
+import android.app.Instrumentation;
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import com.android.internal.app.IVoiceInteractionManagerService;
+
+public class VoiceInteractionService extends Service {
+    /**
+     * The {@link Intent} that must be declared as handled by the service.
+     * To be supported, the service must also require the
+     * {@link android.Manifest.permission#BIND_VOICE_INTERACTION} permission so
+     * that other applications can not abuse it.
+     */
+    @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
+    public static final String SERVICE_INTERFACE =
+            "android.service.voice.VoiceInteractionService";
+
+    /**
+     * Name under which a VoiceInteractionService component publishes information about itself.
+     * This meta-data should reference an XML resource containing a
+     * <code>&lt;{@link
+     * android.R.styleable#VoiceInteractionService voice-interaction-service}&gt;</code> tag.
+     */
+    public static final String SERVICE_META_DATA = "android.voice_interaction";
+
+    IVoiceInteractionService mInterface = new IVoiceInteractionService.Stub() {
+    };
+
+    IVoiceInteractionManagerService mSystemService;
+
+    public void startVoiceActivity(Intent intent, Bundle sessionArgs) {
+        try {
+            mSystemService.startVoiceActivity(intent,
+                    intent.resolveType(getContentResolver()),
+                    mInterface, sessionArgs);
+        } catch (RemoteException e) {
+        }
+    }
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        mSystemService = IVoiceInteractionManagerService.Stub.asInterface(
+                ServiceManager.getService(Context.VOICE_INTERACTION_MANAGER_SERVICE));
+    }
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        if (SERVICE_INTERFACE.equals(intent.getAction())) {
+            return mInterface.asBinder();
+        }
+        return null;
+    }
+}
diff --git a/core/java/android/service/voice/VoiceInteractionServiceInfo.java b/core/java/android/service/voice/VoiceInteractionServiceInfo.java
new file mode 100644
index 0000000..a909ead
--- /dev/null
+++ b/core/java/android/service/voice/VoiceInteractionServiceInfo.java
@@ -0,0 +1,125 @@
+/*
+ * 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.voice;
+
+import android.Manifest;
+import android.content.ComponentName;
+import android.content.pm.PackageManager;
+import android.content.pm.ServiceInfo;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.speech.RecognitionService;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.util.Xml;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+
+/** @hide */
+public class VoiceInteractionServiceInfo {
+    static final String TAG = "VoiceInteractionServiceInfo";
+
+    private String mParseError;
+
+    private ServiceInfo mServiceInfo;
+    private String mSessionService;
+    private String mSettingsActivity;
+
+    public VoiceInteractionServiceInfo(PackageManager pm, ComponentName comp)
+            throws PackageManager.NameNotFoundException {
+        this(pm, pm.getServiceInfo(comp, PackageManager.GET_META_DATA));
+    }
+
+    public VoiceInteractionServiceInfo(PackageManager pm, ServiceInfo si) {
+        if (!Manifest.permission.BIND_VOICE_INTERACTION.equals(si.permission)) {
+            mParseError = "Service does not require permission "
+                    + Manifest.permission.BIND_VOICE_INTERACTION;
+            return;
+        }
+
+        XmlResourceParser parser = null;
+        try {
+            parser = si.loadXmlMetaData(pm, VoiceInteractionService.SERVICE_META_DATA);
+            if (parser == null) {
+                mParseError = "No " + VoiceInteractionService.SERVICE_META_DATA
+                        + " meta-data for " + si.packageName;
+                return;
+            }
+
+            Resources res = pm.getResourcesForApplication(si.applicationInfo);
+
+            AttributeSet attrs = Xml.asAttributeSet(parser);
+
+            int type;
+            while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
+                    && type != XmlPullParser.START_TAG) {
+            }
+
+            String nodeName = parser.getName();
+            if (!"voice-interaction-service".equals(nodeName)) {
+                mParseError = "Meta-data does not start with voice-interaction-service tag";
+                return;
+            }
+
+            TypedArray array = res.obtainAttributes(attrs,
+                    com.android.internal.R.styleable.VoiceInteractionService);
+            mSessionService = array.getString(
+                    com.android.internal.R.styleable.VoiceInteractionService_sessionService);
+            mSettingsActivity = array.getString(
+                    com.android.internal.R.styleable.VoiceInteractionService_settingsActivity);
+            array.recycle();
+            if (mSessionService == null) {
+                mParseError = "No sessionService specified";
+                return;
+            }
+        } catch (XmlPullParserException e) {
+            mParseError = "Error parsing voice interation service meta-data: " + e;
+            Log.w(TAG, "error parsing voice interaction service meta-data", e);
+            return;
+        } catch (IOException e) {
+            mParseError = "Error parsing voice interation service meta-data: " + e;
+            Log.w(TAG, "error parsing voice interaction service meta-data", e);
+            return;
+        } catch (PackageManager.NameNotFoundException e) {
+            mParseError = "Error parsing voice interation service meta-data: " + e;
+            Log.w(TAG, "error parsing voice interaction service meta-data", e);
+            return;
+        } finally {
+            if (parser != null) parser.close();
+        }
+        mServiceInfo = si;
+    }
+
+    public String getParseError() {
+        return mParseError;
+    }
+
+    public ServiceInfo getServiceInfo() {
+        return mServiceInfo;
+    }
+
+    public String getSessionService() {
+        return mSessionService;
+    }
+
+    public String getSettingsActivity() {
+        return mSettingsActivity;
+    }
+}
diff --git a/core/java/android/service/voice/VoiceInteractionSession.java b/core/java/android/service/voice/VoiceInteractionSession.java
new file mode 100644
index 0000000..963b6b4
--- /dev/null
+++ b/core/java/android/service/voice/VoiceInteractionSession.java
@@ -0,0 +1,195 @@
+/**
+ * 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.voice;
+
+import android.content.Context;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.RemoteException;
+import android.util.ArrayMap;
+import android.util.Log;
+import com.android.internal.app.IVoiceInteractor;
+import com.android.internal.app.IVoiceInteractorCallback;
+import com.android.internal.app.IVoiceInteractorRequest;
+import com.android.internal.os.HandlerCaller;
+import com.android.internal.os.SomeArgs;
+
+public abstract class VoiceInteractionSession {
+    static final String TAG = "VoiceInteractionSession";
+    static final boolean DEBUG = true;
+
+    final IVoiceInteractor mInteractor = new IVoiceInteractor.Stub() {
+        @Override
+        public IVoiceInteractorRequest startConfirmation(String callingPackage,
+                IVoiceInteractorCallback callback, String prompt, Bundle extras) {
+            Request request = findRequest(callback, true);
+            mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageOOOO(MSG_START_CONFIRMATION,
+                    new Caller(callingPackage, Binder.getCallingUid()), request,
+                    prompt, extras));
+            return request.mInterface;
+        }
+
+        @Override
+        public IVoiceInteractorRequest startCommand(String callingPackage,
+                IVoiceInteractorCallback callback, String command, Bundle extras) {
+            Request request = findRequest(callback, true);
+            mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageOOOO(MSG_START_COMMAND,
+                    new Caller(callingPackage, Binder.getCallingUid()), request,
+                    command, extras));
+            return request.mInterface;
+        }
+
+        @Override
+        public boolean[] supportsCommands(String callingPackage, String[] commands) {
+            Message msg = mHandlerCaller.obtainMessageIOO(MSG_SUPPORTS_COMMANDS,
+                    0, new Caller(callingPackage, Binder.getCallingUid()), commands);
+            SomeArgs args = mHandlerCaller.sendMessageAndWait(msg);
+            if (args != null) {
+                boolean[] res = (boolean[])args.arg1;
+                args.recycle();
+                return res;
+            }
+            return new boolean[commands.length];
+        }
+    };
+
+    final IVoiceInteractionSession mSession = new IVoiceInteractionSession.Stub() {
+    };
+
+    public static class Request {
+        final IVoiceInteractorRequest mInterface = new IVoiceInteractorRequest.Stub() {
+            @Override
+            public void cancel() throws RemoteException {
+                mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageO(MSG_CANCEL, Request.this));
+            }
+        };
+        final IVoiceInteractorCallback mCallback;
+        final HandlerCaller mHandlerCaller;
+        Request(IVoiceInteractorCallback callback, HandlerCaller handlerCaller) {
+            mCallback = callback;
+            mHandlerCaller = handlerCaller;
+        }
+
+        public void sendConfirmResult(boolean confirmed, Bundle result) {
+            try {
+                if (DEBUG) Log.d(TAG, "sendConfirmResult: req=" + mInterface
+                        + " confirmed=" + confirmed + " result=" + result);
+                mCallback.deliverConfirmationResult(mInterface, confirmed, result);
+            } catch (RemoteException e) {
+            }
+        }
+
+        public void sendCommandResult(boolean complete, Bundle result) {
+            try {
+                if (DEBUG) Log.d(TAG, "sendCommandResult: req=" + mInterface
+                        + " result=" + result);
+                mCallback.deliverCommandResult(mInterface, complete, result);
+            } catch (RemoteException e) {
+            }
+        }
+
+        public void sendCancelResult() {
+            try {
+                if (DEBUG) Log.d(TAG, "sendCancelResult: req=" + mInterface);
+                mCallback.deliverCancel(mInterface);
+            } catch (RemoteException e) {
+            }
+        }
+    }
+
+    public static class Caller {
+        final String packageName;
+        final int uid;
+
+        Caller(String _packageName, int _uid) {
+            packageName = _packageName;
+            uid = _uid;
+        }
+    }
+
+    static final int MSG_START_CONFIRMATION = 1;
+    static final int MSG_START_COMMAND = 2;
+    static final int MSG_SUPPORTS_COMMANDS = 3;
+    static final int MSG_CANCEL = 4;
+
+    final Context mContext;
+    final HandlerCaller mHandlerCaller;
+    final HandlerCaller.Callback mHandlerCallerCallback = new HandlerCaller.Callback() {
+        @Override
+        public void executeMessage(Message msg) {
+            SomeArgs args = (SomeArgs)msg.obj;
+            switch (msg.what) {
+                case MSG_START_CONFIRMATION:
+                    if (DEBUG) Log.d(TAG, "onConfirm: req=" + ((Request) args.arg2).mInterface
+                            + " prompt=" + args.arg3 + " extras=" + args.arg4);
+                    onConfirm((Caller)args.arg1, (Request)args.arg2, (String)args.arg3,
+                            (Bundle)args.arg4);
+                    break;
+                case MSG_START_COMMAND:
+                    if (DEBUG) Log.d(TAG, "onCommand: req=" + ((Request) args.arg2).mInterface
+                            + " command=" + args.arg3 + " extras=" + args.arg4);
+                    onCommand((Caller) args.arg1, (Request) args.arg2, (String) args.arg3,
+                            (Bundle) args.arg4);
+                    break;
+                case MSG_SUPPORTS_COMMANDS:
+                    if (DEBUG) Log.d(TAG, "onGetSupportedCommands: cmds=" + args.arg2);
+                    args.arg1 = onGetSupportedCommands((Caller) args.arg1, (String[]) args.arg2);
+                    break;
+                case MSG_CANCEL:
+                    if (DEBUG) Log.d(TAG, "onCancel: req=" + ((Request) args.arg1).mInterface);
+                    onCancel((Request)args.arg1);
+                    break;
+            }
+        }
+    };
+
+    final ArrayMap<IBinder, Request> mActiveRequests = new ArrayMap<IBinder, Request>();
+
+    public VoiceInteractionSession(Context context) {
+        this(context, new Handler());
+    }
+
+    public VoiceInteractionSession(Context context, Handler handler) {
+        mContext = context;
+        mHandlerCaller = new HandlerCaller(context, handler.getLooper(),
+                mHandlerCallerCallback, true);
+    }
+
+    Request findRequest(IVoiceInteractorCallback callback, boolean newRequest) {
+        synchronized (this) {
+            Request req = mActiveRequests.get(callback.asBinder());
+            if (req != null) {
+                if (newRequest) {
+                    throw new IllegalArgumentException("Given request callback " + callback
+                            + " is already active");
+                }
+                return req;
+            }
+            req = new Request(callback, mHandlerCaller);
+            mActiveRequests.put(callback.asBinder(), req);
+            return req;
+        }
+    }
+
+    public abstract boolean[] onGetSupportedCommands(Caller caller, String[] commands);
+    public abstract void onConfirm(Caller caller, Request request, String prompt, Bundle extras);
+    public abstract void onCommand(Caller caller, Request request, String command, Bundle extras);
+    public abstract void onCancel(Request request);
+}
diff --git a/core/java/android/service/voice/VoiceInteractionSessionService.java b/core/java/android/service/voice/VoiceInteractionSessionService.java
new file mode 100644
index 0000000..40e5bba
--- /dev/null
+++ b/core/java/android/service/voice/VoiceInteractionSessionService.java
@@ -0,0 +1,82 @@
+/**
+ * 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.voice;
+
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import com.android.internal.app.IVoiceInteractionManagerService;
+import com.android.internal.os.HandlerCaller;
+import com.android.internal.os.SomeArgs;
+
+public abstract class VoiceInteractionSessionService extends Service {
+
+    static final int MSG_NEW_SESSION = 1;
+
+    IVoiceInteractionManagerService mSystemService;
+
+    IVoiceInteractionSessionService mInterface = new IVoiceInteractionSessionService.Stub() {
+        public void newSession(IBinder token, Bundle args) {
+            mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageOO(MSG_NEW_SESSION,
+                    token, args));
+
+        }
+    };
+
+    HandlerCaller mHandlerCaller;
+    final HandlerCaller.Callback mHandlerCallerCallback = new HandlerCaller.Callback() {
+        @Override
+        public void executeMessage(Message msg) {
+            SomeArgs args = (SomeArgs)msg.obj;
+            switch (msg.what) {
+                case MSG_NEW_SESSION:
+                    doNewSession((IBinder)args.arg1, (Bundle)args.arg2);
+                    break;
+            }
+        }
+    };
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        mSystemService = IVoiceInteractionManagerService.Stub.asInterface(
+                ServiceManager.getService(Context.VOICE_INTERACTION_MANAGER_SERVICE));
+        mHandlerCaller = new HandlerCaller(this, Looper.myLooper(),
+                mHandlerCallerCallback, true);
+    }
+
+    public abstract VoiceInteractionSession onNewSession(Bundle args);
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        return mInterface.asBinder();
+    }
+
+    void doNewSession(IBinder token, Bundle args) {
+        VoiceInteractionSession session = onNewSession(args);
+        try {
+            mSystemService.deliverNewSession(token, session.mSession, session.mInteractor);
+        } catch (RemoteException e) {
+        }
+    }
+}
diff --git a/core/java/android/transition/Fade.java b/core/java/android/transition/Fade.java
index 08e27d3..e70dc0c 100644
--- a/core/java/android/transition/Fade.java
+++ b/core/java/android/transition/Fade.java
@@ -105,7 +105,7 @@
         if (DBG) {
             Log.d(LOG_TAG, "Created animator " + anim);
         }
-        FadeAnimatorListener listener = new FadeAnimatorListener(view, endAlpha);
+        FadeAnimatorListener listener = new FadeAnimatorListener(view);
         anim.addListener(listener);
         anim.addPauseListener(listener);
         return anim;
@@ -138,13 +138,11 @@
 
     private static class FadeAnimatorListener extends AnimatorListenerAdapter {
         private final View mView;
-        private final float mEndAlpha;
         private boolean mCanceled = false;
-        private float mPausedAlpha;
+        private float mPausedAlpha = -1;
 
-        public FadeAnimatorListener(View view, float endAlpha) {
+        public FadeAnimatorListener(View view) {
             mView = view;
-            mEndAlpha = endAlpha;
         }
 
         @Override
@@ -158,14 +156,14 @@
         @Override
         public void onAnimationEnd(Animator animator) {
             if (!mCanceled) {
-                mView.setTransitionAlpha(mEndAlpha);
+                mView.setTransitionAlpha(1);
             }
         }
 
         @Override
         public void onAnimationPause(Animator animator) {
             mPausedAlpha = mView.getTransitionAlpha();
-            mView.setTransitionAlpha(mEndAlpha);
+            mView.setTransitionAlpha(1);
         }
 
         @Override
diff --git a/core/java/android/transition/Visibility.java b/core/java/android/transition/Visibility.java
index 526803a..6e6496c 100644
--- a/core/java/android/transition/Visibility.java
+++ b/core/java/android/transition/Visibility.java
@@ -347,10 +347,11 @@
         }
 
         if (viewToKeep != null) {
+            int originalVisibility = viewToKeep.getVisibility();
             viewToKeep.setVisibility(View.VISIBLE);
             Animator animator = onDisappear(sceneRoot, viewToKeep, startValues, endValues);
             if (animator == null) {
-                viewToKeep.setVisibility(finalVisibility);
+                viewToKeep.setVisibility(originalVisibility);
             } else {
                 final View finalViewToKeep = viewToKeep;
                 animator.addListener(new AnimatorListenerAdapter() {
diff --git a/core/java/android/util/EventLog.java b/core/java/android/util/EventLog.java
index 29c0ba2..aefced8 100644
--- a/core/java/android/util/EventLog.java
+++ b/core/java/android/util/EventLog.java
@@ -56,16 +56,18 @@
     public static final class Event {
         private final ByteBuffer mBuffer;
 
-        // Layout of event log entry received from kernel.
+        // Layout of event log entry received from Android logger.
+        //  see system/core/include/log/logger.h
         private static final int LENGTH_OFFSET = 0;
+        private static final int HEADER_SIZE_OFFSET = 2;
         private static final int PROCESS_OFFSET = 4;
         private static final int THREAD_OFFSET = 8;
         private static final int SECONDS_OFFSET = 12;
         private static final int NANOSECONDS_OFFSET = 16;
 
-        private static final int PAYLOAD_START = 20;
-        private static final int TAG_OFFSET = 20;
-        private static final int DATA_START = 24;
+        // Layout for event log v1 format, v2 and v3 use HEADER_SIZE_OFFSET
+        private static final int V1_PAYLOAD_START = 20;
+        private static final int DATA_OFFSET = 4;
 
         // Value types
         private static final byte INT_TYPE    = 0;
@@ -97,14 +99,22 @@
 
         /** @return the type tag code of the entry */
         public int getTag() {
-            return mBuffer.getInt(TAG_OFFSET);
+            int offset = mBuffer.getShort(HEADER_SIZE_OFFSET);
+            if (offset == 0) {
+                offset = V1_PAYLOAD_START;
+            }
+            return mBuffer.getInt(offset);
         }
 
         /** @return one of Integer, Long, String, null, or Object[] of same. */
         public synchronized Object getData() {
             try {
-                mBuffer.limit(PAYLOAD_START + mBuffer.getShort(LENGTH_OFFSET));
-                mBuffer.position(DATA_START);  // Just after the tag.
+                int offset = mBuffer.getShort(HEADER_SIZE_OFFSET);
+                if (offset == 0) {
+                    offset = V1_PAYLOAD_START;
+                }
+                mBuffer.limit(offset + mBuffer.getShort(LENGTH_OFFSET));
+                mBuffer.position(offset + DATA_OFFSET); // Just after the tag.
                 return decodeObject();
             } catch (IllegalArgumentException e) {
                 Log.wtf(TAG, "Illegal entry payload: tag=" + getTag(), e);
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 84d1328..fdf31fa 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -2375,24 +2375,19 @@
     static final int PFLAG3_CALLED_SUPER = 0x10;
 
     /**
-     * Flag indicating that an view will be clipped to its outline.
-     */
-    static final int PFLAG3_CLIP_TO_OUTLINE = 0x20;
-
-    /**
      * Flag indicating that a view's outline has been specifically defined.
      */
-    static final int PFLAG3_OUTLINE_DEFINED = 0x40;
+    static final int PFLAG3_OUTLINE_DEFINED = 0x20;
 
     /**
      * Flag indicating that we're in the process of applying window insets.
      */
-    static final int PFLAG3_APPLYING_INSETS = 0x80;
+    static final int PFLAG3_APPLYING_INSETS = 0x40;
 
     /**
      * Flag indicating that we're in the process of fitting system windows using the old method.
      */
-    static final int PFLAG3_FITTING_SYSTEM_WINDOWS = 0x100;
+    static final int PFLAG3_FITTING_SYSTEM_WINDOWS = 0x80;
 
     /**
      * Flag indicating that nested scrolling is enabled for this view.
@@ -3258,9 +3253,7 @@
 
     /**
      * Stores the outline of the view, passed down to the DisplayList level for
-     * defining shadow shape and clipping.
-     *
-     * TODO: once RenderNode is long-lived, remove this and rely on native copy.
+     * defining shadow shape.
      */
     private Outline mOutline;
 
@@ -10572,16 +10565,13 @@
 
     /**
      * Sets the outline of the view, which defines the shape of the shadow it
-     * casts, and can used for clipping.
+     * casts.
      * <p>
      * If the outline is not set or is null, shadows will be cast from the
-     * bounds of the View, and clipToOutline will be ignored.
+     * bounds of the View.
      *
      * @param outline The new outline of the view.
      *         Must be {@link android.graphics.Outline#isValid() valid.}
-     *
-     * @see #getClipToOutline()
-     * @see #setClipToOutline(boolean)
      */
     public void setOutline(@Nullable Outline outline) {
         if (outline != null && !outline.isValid()) {
@@ -10595,46 +10585,33 @@
         } else {
             // always copy the path since caller may reuse
             if (mOutline == null) {
-                mOutline = new Outline();
+                mOutline = new Outline(outline);
             }
-            mOutline.set(outline);
         }
         mRenderNode.setOutline(mOutline);
     }
 
-    /**
-     * Returns whether the outline of the View will be used for clipping.
-     *
-     * @see #setOutline(Outline)
-     */
-    public final boolean getClipToOutline() {
-        return ((mPrivateFlags3 & PFLAG3_CLIP_TO_OUTLINE) != 0);
-    }
+    // TODO: remove
+    public final boolean getClipToOutline() { return false; }
+    public void setClipToOutline(boolean clipToOutline) {}
 
-    /**
-     * Sets whether the outline of the View will be used for clipping.
-     * <p>
-     * The current implementation of outline clipping uses
-     * {@link Canvas#clipPath(Path) path clipping},
-     * and thus does not support anti-aliasing, and is expensive in terms of
-     * graphics performance. Therefore, it is strongly recommended that this
-     * property only be set temporarily, as in an animation. For the same
-     * reasons, there is no parallel XML attribute for this property.
-     * <p>
-     * If the outline of the view is not set or is empty, no clipping will be
-     * performed.
-     *
-     * @see #setOutline(Outline)
-     */
-    public void setClipToOutline(boolean clipToOutline) {
-        // TODO : Add a fast invalidation here.
-        if (getClipToOutline() != clipToOutline) {
-            if (clipToOutline) {
-                mPrivateFlags3 |= PFLAG3_CLIP_TO_OUTLINE;
+    private void queryOutlineFromBackgroundIfUndefined() {
+        if ((mPrivateFlags3 & PFLAG3_OUTLINE_DEFINED) == 0) {
+            // Outline not currently defined, query from background
+            if (mOutline == null) {
+                mOutline = new Outline();
             } else {
-                mPrivateFlags3 &= ~PFLAG3_CLIP_TO_OUTLINE;
+                //invalidate outline, to ensure background calculates it
+                mOutline.set(null);
             }
-            mRenderNode.setClipToOutline(clipToOutline);
+            if (mBackground.getOutline(mOutline)) {
+                if (!mOutline.isValid()) {
+                    throw new IllegalStateException("Background drawable failed to build outline");
+                }
+                mRenderNode.setOutline(mOutline);
+            } else {
+                mRenderNode.setOutline(null);
+            }
         }
     }
 
@@ -12784,6 +12761,10 @@
         mPrivateFlags &= ~PFLAG_CANCEL_NEXT_UP_EVENT;
         mPrivateFlags3 &= ~PFLAG3_IS_LAID_OUT;
 
+        if (mBackground != null) {
+            mBackground.clearHotspots();
+        }
+
         removeUnsetPressCallback();
         removeLongPressCallback();
         removePerformClickCallback();
@@ -14893,11 +14874,7 @@
         if (mBackgroundSizeChanged) {
             background.setBounds(0, 0,  mRight - mLeft, mBottom - mTop);
             mBackgroundSizeChanged = false;
-            if ((mPrivateFlags3 & PFLAG3_OUTLINE_DEFINED) == 0) {
-                // Outline not currently define, query from background
-                mOutline = background.getOutline();
-                mRenderNode.setOutline(mOutline);
-            }
+            queryOutlineFromBackgroundIfUndefined();
         }
 
         // Attempt to use a display list if requested.
@@ -15299,7 +15276,7 @@
      * @param drawable the drawable to invalidate
      */
     @Override
-    public void invalidateDrawable(Drawable drawable) {
+    public void invalidateDrawable(@NonNull Drawable drawable) {
         if (verifyDrawable(drawable)) {
             final Rect dirty = drawable.getDirtyBounds();
             final int scrollX = mScrollX;
@@ -15307,6 +15284,10 @@
 
             invalidate(dirty.left + scrollX, dirty.top + scrollY,
                     dirty.right + scrollX, dirty.bottom + scrollY);
+
+            if (drawable == mBackground) {
+                queryOutlineFromBackgroundIfUndefined();
+            }
         }
     }
 
@@ -18004,7 +17985,7 @@
      *
      * <p>If <code>startNestedScroll</code> returns true, a cooperative parent was found.
      * If it returns false the caller may ignore the rest of this contract until the next scroll.
-     * </p>
+     * Calling startNestedScroll while a nested scroll is already in progress will return true.</p>
      *
      * <p>At each incremental step of the scroll the caller should invoke
      * {@link #dispatchNestedPreScroll(int, int, int[], int[]) dispatchNestedPreScroll}
@@ -18028,6 +18009,10 @@
      * @see #dispatchNestedScroll(int, int, int, int, int[])
      */
     public boolean startNestedScroll(int axes) {
+        if (hasNestedScrollingParent()) {
+            // Already in progress
+            return true;
+        }
         if (isNestedScrollingEnabled()) {
             ViewParent p = getParent();
             View child = this;
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index ad76145..8865ab4 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -2342,6 +2342,7 @@
 
         if (disallowIntercept) {
             mGroupFlags |= FLAG_DISALLOW_INTERCEPT;
+            stopNestedScroll();
         } else {
             mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;
         }
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index cf9539e..c914e52 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -1482,6 +1482,7 @@
      * {@link KeyChain.ACTION_STORAGE_CHANGED}
      *
      * @param resultCallback A callback to be invoked when client certs are cleared.
+     *                       The embedder can pass null if not interested in the callback.
      *
      * TODO(sgurun) unhide
      * @hide
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 301317e..becda67 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -543,7 +543,7 @@
     /**
      * The last CheckForTap runnable we posted, if any
      */
-    private Runnable mPendingCheckForTap;
+    private CheckForTap mPendingCheckForTap;
 
     /**
      * The last CheckForKeyLongPress runnable we posted, if any
@@ -2126,7 +2126,9 @@
     @Override
     protected void onLayout(boolean changed, int l, int t, int r, int b) {
         super.onLayout(changed, l, t, r, b);
+
         mInLayout = true;
+
         final int childCount = getChildCount();
         if (changed) {
             for (int i = 0; i < childCount; i++) {
@@ -3185,7 +3187,10 @@
         return INVALID_ROW_ID;
     }
 
-    final class CheckForTap implements Runnable {
+    private final class CheckForTap implements Runnable {
+        float x;
+        float y;
+
         @Override
         public void run() {
             if (mTouchMode == TOUCH_MODE_DOWN) {
@@ -3205,7 +3210,7 @@
                         final boolean longClickable = isLongClickable();
 
                         if (mSelector != null) {
-                            Drawable d = mSelector.getCurrent();
+                            final Drawable d = mSelector.getCurrent();
                             if (d != null && d instanceof TransitionDrawable) {
                                 if (longClickable) {
                                     ((TransitionDrawable) d).startTransition(longPressTimeout);
@@ -3213,6 +3218,9 @@
                                     ((TransitionDrawable) d).resetTransition();
                                 }
                             }
+                            if (d.supportsHotspots()) {
+                                d.setHotspot(R.attr.state_pressed, x, y);
+                            }
                         }
 
                         if (longClickable) {
@@ -3596,6 +3604,8 @@
                         mPendingCheckForTap = new CheckForTap();
                     }
 
+                    mPendingCheckForTap.x = ev.getX();
+                    mPendingCheckForTap.y = ev.getY();
                     postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());
                 }
             }
@@ -3705,6 +3715,9 @@
                                 if (d != null && d instanceof TransitionDrawable) {
                                     ((TransitionDrawable) d).resetTransition();
                                 }
+                                if (mSelector.supportsHotspots()) {
+                                    mSelector.setHotspot(R.attr.state_pressed, x, ev.getY());
+                                }
                             }
                             if (mTouchModeReset != null) {
                                 removeCallbacks(mTouchModeReset);
@@ -3716,6 +3729,9 @@
                                     mTouchMode = TOUCH_MODE_REST;
                                     child.setPressed(false);
                                     setPressed(false);
+                                    if (mSelector != null && mSelector.supportsHotspots()) {
+                                        mSelector.removeHotspot(R.attr.state_pressed);
+                                    }
                                     if (!mDataChanged && !mIsDetaching && isAttachedToWindow()) {
                                         performClick.run();
                                     }
diff --git a/core/java/android/widget/ScrollView.java b/core/java/android/widget/ScrollView.java
index 34a6a40..7e8f6b4 100644
--- a/core/java/android/widget/ScrollView.java
+++ b/core/java/android/widget/ScrollView.java
@@ -553,6 +553,7 @@
                 if (mIsBeingDragged && mScrollStrictSpan == null) {
                     mScrollStrictSpan = StrictMode.enterCriticalSpan("ScrollView-scroll");
                 }
+                startNestedScroll(SCROLL_AXIS_VERTICAL);
                 break;
             }
 
@@ -565,6 +566,7 @@
                 if (mScroller.springBack(mScrollX, mScrollY, 0, 0, 0, getScrollRange())) {
                     postInvalidateOnAnimation();
                 }
+                stopNestedScroll();
                 break;
             case MotionEvent.ACTION_POINTER_UP:
                 onSecondaryPointerUp(ev);
diff --git a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
new file mode 100644
index 0000000..3219ddd
--- /dev/null
+++ b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.app;
+
+import android.content.Intent;
+import android.os.Bundle;
+
+import com.android.internal.app.IVoiceInteractor;
+import android.service.voice.IVoiceInteractionService;
+import android.service.voice.IVoiceInteractionSession;
+
+interface IVoiceInteractionManagerService {
+    void startVoiceActivity(in Intent intent, String resolvedType, IVoiceInteractionService service,
+            in Bundle sessionArgs);
+    int deliverNewSession(IBinder token, IVoiceInteractionSession session,
+            IVoiceInteractor interactor);
+}
diff --git a/core/java/com/android/internal/app/IVoiceInteractor.aidl b/core/java/com/android/internal/app/IVoiceInteractor.aidl
new file mode 100644
index 0000000..737906a
--- /dev/null
+++ b/core/java/com/android/internal/app/IVoiceInteractor.aidl
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.app;
+
+import android.os.Bundle;
+
+import com.android.internal.app.IVoiceInteractorCallback;
+import com.android.internal.app.IVoiceInteractorRequest;
+
+/**
+ * IPC interface for an application to perform calls through a VoiceInteractor.
+ */
+interface IVoiceInteractor {
+    IVoiceInteractorRequest startConfirmation(String callingPackage,
+            IVoiceInteractorCallback callback, String prompt, in Bundle extras);
+    IVoiceInteractorRequest startCommand(String callingPackage,
+            IVoiceInteractorCallback callback, String command, in Bundle extras);
+    boolean[] supportsCommands(String callingPackage, in String[] commands);
+}
diff --git a/core/java/com/android/internal/app/IVoiceInteractorCallback.aidl b/core/java/com/android/internal/app/IVoiceInteractorCallback.aidl
new file mode 100644
index 0000000..c6f93e1
--- /dev/null
+++ b/core/java/com/android/internal/app/IVoiceInteractorCallback.aidl
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.app;
+
+import android.os.Bundle;
+
+import com.android.internal.app.IVoiceInteractorRequest;
+
+/**
+ * IPC interface for an application to receive callbacks from the voice system.
+ */
+oneway interface IVoiceInteractorCallback {
+    void deliverConfirmationResult(IVoiceInteractorRequest request, boolean confirmed,
+            in Bundle result);
+    void deliverCommandResult(IVoiceInteractorRequest request, boolean complete, in Bundle result);
+    void deliverCancel(IVoiceInteractorRequest request);
+}
diff --git a/core/java/com/android/internal/app/IVoiceInteractorRequest.aidl b/core/java/com/android/internal/app/IVoiceInteractorRequest.aidl
new file mode 100644
index 0000000..ce2902d
--- /dev/null
+++ b/core/java/com/android/internal/app/IVoiceInteractorRequest.aidl
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.app;
+
+/**
+ * IPC interface identifying a request from an application calling through an IVoiceInteractor.
+ */
+interface IVoiceInteractorRequest {
+    void cancel();
+}
diff --git a/core/java/com/android/internal/os/HandlerCaller.java b/core/java/com/android/internal/os/HandlerCaller.java
index d9e3ef6..40834ba 100644
--- a/core/java/com/android/internal/os/HandlerCaller.java
+++ b/core/java/com/android/internal/os/HandlerCaller.java
@@ -85,7 +85,27 @@
     public void sendMessage(Message msg) {
         mH.sendMessage(msg);
     }
-    
+
+    public SomeArgs sendMessageAndWait(Message msg) {
+        if (Looper.myLooper() == mH.getLooper()) {
+            throw new IllegalStateException("Can't wait on same thread as looper");
+        }
+        SomeArgs args = (SomeArgs)msg.obj;
+        args.mWaitState = SomeArgs.WAIT_WAITING;
+        mH.sendMessage(msg);
+        synchronized (args) {
+            while (args.mWaitState == SomeArgs.WAIT_WAITING) {
+                try {
+                    args.wait();
+                } catch (InterruptedException e) {
+                    return null;
+                }
+            }
+        }
+        args.mWaitState = SomeArgs.WAIT_NONE;
+        return args;
+    }
+
     public Message obtainMessage(int what) {
         return mH.obtainMessage(what);
     }
@@ -136,6 +156,14 @@
         return mH.obtainMessage(what, arg1, 0, args);
     }
     
+    public Message obtainMessageIOOO(int what, int arg1, Object arg2, Object arg3, Object arg4) {
+        SomeArgs args = SomeArgs.obtain();
+        args.arg1 = arg2;
+        args.arg2 = arg3;
+        args.arg3 = arg4;
+        return mH.obtainMessage(what, arg1, 0, args);
+    }
+
     public Message obtainMessageOO(int what, Object arg1, Object arg2) {
         SomeArgs args = SomeArgs.obtain();
         args.arg1 = arg1;
@@ -161,6 +189,17 @@
         return mH.obtainMessage(what, 0, 0, args);
     }
     
+    public Message obtainMessageOOOOO(int what, Object arg1, Object arg2,
+            Object arg3, Object arg4, Object arg5) {
+        SomeArgs args = SomeArgs.obtain();
+        args.arg1 = arg1;
+        args.arg2 = arg2;
+        args.arg3 = arg3;
+        args.arg4 = arg4;
+        args.arg5 = arg5;
+        return mH.obtainMessage(what, 0, 0, args);
+    }
+
     public Message obtainMessageIIII(int what, int arg1, int arg2,
             int arg3, int arg4) {
         SomeArgs args = SomeArgs.obtain();
diff --git a/core/java/com/android/internal/os/SomeArgs.java b/core/java/com/android/internal/os/SomeArgs.java
index 6fb72f1..7edf4cc 100644
--- a/core/java/com/android/internal/os/SomeArgs.java
+++ b/core/java/com/android/internal/os/SomeArgs.java
@@ -35,6 +35,11 @@
 
     private boolean mInPool;
 
+    static final int WAIT_NONE = 0;
+    static final int WAIT_WAITING = 1;
+    static final int WAIT_FINISHED = 2;
+    int mWaitState = WAIT_NONE;
+
     public Object arg1;
     public Object arg2;
     public Object arg3;
@@ -70,6 +75,9 @@
         if (mInPool) {
             throw new IllegalStateException("Already recycled.");
         }
+        if (mWaitState != WAIT_NONE) {
+            return;
+        }
         synchronized (sPoolLock) {
             clear();
             if (sPoolSize < MAX_POOL_SIZE) {
diff --git a/core/java/com/android/server/SystemService.java b/core/java/com/android/server/SystemService.java
index e374563..bf36bb1 100644
--- a/core/java/com/android/server/SystemService.java
+++ b/core/java/com/android/server/SystemService.java
@@ -123,6 +123,38 @@
     public void onBootPhase(int phase) {}
 
     /**
+     * Called when a new user is starting, for system services to initialize any per-user
+     * state they maintain for running users.
+     * @param userHandle The identifier of the user.
+     */
+    public void onStartUser(int userHandle) {}
+
+    /**
+     * Called when switching to a different foreground user, for system services that have
+     * special behavior for whichever user is currently in the foreground.  This is called
+     * before any application processes are aware of the new user.
+     * @param userHandle The identifier of the user.
+     */
+    public void onSwitchUser(int userHandle) {}
+
+    /**
+     * Called when an existing user is stopping, for system services to finalize any per-user
+     * state they maintain for running users.  This is called prior to sending the SHUTDOWN
+     * broadcast to the user; it is a good place to stop making use of any resources of that
+     * user (such as binding to a service running in the user).
+     * @param userHandle The identifier of the user.
+     */
+    public void onStopUser(int userHandle) {}
+
+    /**
+     * Called when an existing user is stopping, for system services to finalize any per-user
+     * state they maintain for running users.  This is called after all application process
+     * teardown of the user is complete.
+     * @param userHandle The identifier of the user.
+     */
+    public void onCleanupUser(int userHandle) {}
+
+    /**
      * Publish the service so it is accessible to other services and apps.
      */
     protected final void publishBinderService(String name, IBinder service) {
diff --git a/core/java/com/android/server/SystemServiceManager.java b/core/java/com/android/server/SystemServiceManager.java
index eb8df0e..87a50a9 100644
--- a/core/java/com/android/server/SystemServiceManager.java
+++ b/core/java/com/android/server/SystemServiceManager.java
@@ -131,6 +131,58 @@
         }
     }
 
+    public void startUser(final int userHandle) {
+        final int serviceLen = mServices.size();
+        for (int i = 0; i < serviceLen; i++) {
+            final SystemService service = mServices.get(i);
+            try {
+                service.onStartUser(userHandle);
+            } catch (Exception ex) {
+                Slog.wtf(TAG, "Failure reporting start of user " + userHandle
+                        + " to service " + service.getClass().getName(), ex);
+            }
+        }
+    }
+
+    public void switchUser(final int userHandle) {
+        final int serviceLen = mServices.size();
+        for (int i = 0; i < serviceLen; i++) {
+            final SystemService service = mServices.get(i);
+            try {
+                service.onSwitchUser(userHandle);
+            } catch (Exception ex) {
+                Slog.wtf(TAG, "Failure reporting switch of user " + userHandle
+                        + " to service " + service.getClass().getName(), ex);
+            }
+        }
+    }
+
+    public void stopUser(final int userHandle) {
+        final int serviceLen = mServices.size();
+        for (int i = 0; i < serviceLen; i++) {
+            final SystemService service = mServices.get(i);
+            try {
+                service.onStopUser(userHandle);
+            } catch (Exception ex) {
+                Slog.wtf(TAG, "Failure reporting stop of user " + userHandle
+                        + " to service " + service.getClass().getName(), ex);
+            }
+        }
+    }
+
+    public void cleanupUser(final int userHandle) {
+        final int serviceLen = mServices.size();
+        for (int i = 0; i < serviceLen; i++) {
+            final SystemService service = mServices.get(i);
+            try {
+                service.onCleanupUser(userHandle);
+            } catch (Exception ex) {
+                Slog.wtf(TAG, "Failure reporting cleanup of user " + userHandle
+                        + " to service " + service.getClass().getName(), ex);
+            }
+        }
+    }
+
     /** Sets the safe mode flag for services to query. */
     public void setSafeMode(boolean safeMode) {
         mSafeMode = safeMode;
diff --git a/core/jni/android/graphics/Path.cpp b/core/jni/android/graphics/Path.cpp
index 1ad1a8a..420a17f 100644
--- a/core/jni/android/graphics/Path.cpp
+++ b/core/jni/android/graphics/Path.cpp
@@ -163,42 +163,35 @@
         SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
         obj->close();
     }
- 
-    static void addRect__RectFI(JNIEnv* env, jobject clazz, jlong objHandle, jobject jrect, jint dirHandle) {
-        SkRect rect;
-        SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
-        SkPath::Direction dir = static_cast<SkPath::Direction>(dirHandle);
-        GraphicsJNI::jrectf_to_rect(env, jrect, &rect);
-        obj->addRect(rect, dir);
-    }
- 
-    static void addRect__FFFFI(JNIEnv* env, jobject clazz, jlong objHandle, jfloat left, jfloat top, jfloat right, jfloat bottom, jint dirHandle) {
+
+    static void addRect(JNIEnv* env, jobject clazz, jlong objHandle,
+            jfloat left, jfloat top, jfloat right, jfloat bottom, jint dirHandle) {
         SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
         SkPath::Direction dir = static_cast<SkPath::Direction>(dirHandle);
         obj->addRect(left, top, right, bottom, dir);
     }
- 
-    static void addOval(JNIEnv* env, jobject clazz, jlong objHandle, jobject oval, jint dirHandle) {
+
+    static void addOval(JNIEnv* env, jobject clazz, jlong objHandle,
+            jfloat left, jfloat top, jfloat right, jfloat bottom, jint dirHandle) {
         SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
         SkPath::Direction dir = static_cast<SkPath::Direction>(dirHandle);
-        SkRect oval_;
-        GraphicsJNI::jrectf_to_rect(env, oval, &oval_);
-        obj->addOval(oval_, dir);
+        SkRect oval = SkRect::MakeLTRB(left, top, right, bottom);
+        obj->addOval(oval, dir);
     }
- 
+
     static void addCircle(JNIEnv* env, jobject clazz, jlong objHandle, jfloat x, jfloat y, jfloat radius, jint dirHandle) {
         SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
         SkPath::Direction dir = static_cast<SkPath::Direction>(dirHandle);
         obj->addCircle(x, y, radius, dir);
     }
- 
+
     static void addArc(JNIEnv* env, jobject clazz, jlong objHandle, jobject oval, jfloat startAngle, jfloat sweepAngle) {
         SkRect oval_;
         SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
         GraphicsJNI::jrectf_to_rect(env, oval, &oval_);
         obj->addArc(oval_, startAngle, sweepAngle);
     }
- 
+
     static void addRoundRectXY(JNIEnv* env, jobject clazz, jlong objHandle, jobject jrect,
             jfloat rx, jfloat ry, jint dirHandle) {
         SkRect rect;
@@ -496,9 +489,8 @@
     {"native_rCubicTo","(JFFFFFF)V", (void*) SkPathGlue::rCubicTo},
     {"native_arcTo","(JLandroid/graphics/RectF;FFZ)V", (void*) SkPathGlue::arcTo},
     {"native_close","(J)V", (void*) SkPathGlue::close},
-    {"native_addRect","(JLandroid/graphics/RectF;I)V", (void*) SkPathGlue::addRect__RectFI},
-    {"native_addRect","(JFFFFI)V", (void*) SkPathGlue::addRect__FFFFI},
-    {"native_addOval","(JLandroid/graphics/RectF;I)V", (void*) SkPathGlue::addOval},
+    {"native_addRect","(JFFFFI)V", (void*) SkPathGlue::addRect},
+    {"native_addOval","(JFFFFI)V", (void*) SkPathGlue::addOval},
     {"native_addCircle","(JFFFI)V", (void*) SkPathGlue::addCircle},
     {"native_addArc","(JLandroid/graphics/RectF;FF)V", (void*) SkPathGlue::addArc},
     {"native_addRoundRect","(JLandroid/graphics/RectF;FFI)V", (void*) SkPathGlue::addRoundRectXY},
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 8141a00..c293c7a 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -508,7 +508,7 @@
     nsecs_t presentedTimesNanoSrc[frameCount];
 
     for (size_t i = 0; i < frameCount; i++) {
-        nsecs_t presentedTimeNano = stats.desiredPresentTimesNano[i];
+        nsecs_t presentedTimeNano = stats.actualPresentTimesNano[i];
         if (presentedTimeNano == INT64_MAX) {
             presentedTimeNano = gWindowContentFrameStatsClassInfo.UNDEFINED_TIME_NANO;
         }
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 57e845f..4f093a8 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2056,6 +2056,13 @@
         android:description="@string/permdesc_bindWallpaper"
         android:protectionLevel="signature|system" />
 
+    <!-- Must be required by a {@link android.service.voice.VoiceInteractionService},
+         to ensure that only the system can bind to it. -->
+    <permission android:name="android.permission.BIND_VOICE_INTERACTION"
+        android:label="@string/permlab_bindVoiceInteraction"
+        android:description="@string/permdesc_bindVoiceInteraction"
+        android:protectionLevel="signature" />
+
     <!-- Must be required by a {@link com.android.media.remotedisplay.RemoteDisplayProvider},
          to ensure that only the system can bind to it.
          @hide -->
@@ -2620,6 +2627,15 @@
         android:description="@string/permdesc_bindNotificationListenerService"
         android:protectionLevel="signature" />
 
+    <!-- Must be required by an {@link
+         android.service.notification.ConditionProviderService},
+         to ensure that only the system can bind to it.
+         @hide -->
+    <permission android:name="android.permission.BIND_CONDITION_PROVIDER_SERVICE"
+        android:label="@string/permlab_bindConditionProviderService"
+        android:description="@string/permdesc_bindConditionProviderService"
+        android:protectionLevel="signature" />
+
     <!-- Allows an application to call into a carrier setup flow. It is up to the
          carrier setup application to enforce that this permission is required
          @hide This is not a third-party API (intended for OEMs and system apps). -->
diff --git a/core/res/res/drawable/btn_borderless_quantum.xml b/core/res/res/drawable/btn_borderless_quantum.xml
index 69a891a..2e3c515 100644
--- a/core/res/res/drawable/btn_borderless_quantum.xml
+++ b/core/res/res/drawable/btn_borderless_quantum.xml
@@ -16,7 +16,6 @@
 
 <touch-feedback xmlns:android="http://schemas.android.com/apk/res/android"
     android:tint="?attr/colorButtonPressed">
-    <item android:drawable="@color/transparent" />
     <item android:id="@id/mask"
         android:drawable="@drawable/btn_qntm_alpha" />
 </touch-feedback>
diff --git a/core/res/res/drawable/dialog_background_quantum.xml b/core/res/res/drawable/dialog_background_quantum.xml
new file mode 100644
index 0000000..7e5b003
--- /dev/null
+++ b/core/res/res/drawable/dialog_background_quantum.xml
@@ -0,0 +1,25 @@
+<?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.
+-->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle">
+
+    <corners
+        android:radius="2dp" />
+    <solid
+        android:color="?attr/colorBackground" />
+
+</shape>
diff --git a/core/res/res/drawable/list_selector_quantum.xml b/core/res/res/drawable/list_selector_quantum.xml
new file mode 100644
index 0000000..c007117
--- /dev/null
+++ b/core/res/res/drawable/list_selector_quantum.xml
@@ -0,0 +1,22 @@
+<?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.
+-->
+
+<touch-feedback xmlns:android="http://schemas.android.com/apk/res/android"
+    android:tint="?attr/colorButtonPressed">
+    <item android:id="@id/mask">
+        <color android:color="@color/white" />
+    </item>
+</touch-feedback>
diff --git a/core/res/res/layout/alert_dialog_quantum.xml b/core/res/res/layout/alert_dialog_quantum.xml
index 537162a..80e34cb 100644
--- a/core/res/res/layout/alert_dialog_quantum.xml
+++ b/core/res/res/layout/alert_dialog_quantum.xml
@@ -20,7 +20,10 @@
     android:id="@+id/parentPanel"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    android:orientation="vertical">
+    android:orientation="vertical"
+    android:background="@drawable/dialog_background_quantum"
+    android:translationZ="@dimen/floating_window_z"
+    android:layout_margin="@dimen/floating_window_margin">
 
     <LinearLayout android:id="@+id/topPanel"
         android:layout_width="match_parent"
diff --git a/core/res/res/layout/simple_list_item_2_single_choice.xml b/core/res/res/layout/simple_list_item_2_single_choice.xml
index 65c5856..940c6b8 100644
--- a/core/res/res/layout/simple_list_item_2_single_choice.xml
+++ b/core/res/res/layout/simple_list_item_2_single_choice.xml
@@ -21,7 +21,8 @@
     android:gravity="center_vertical"
     android:paddingStart="16dip"
     android:paddingEnd="12dip"
-    android:minHeight="?android:attr/listPreferredItemHeightSmall">
+    android:minHeight="?attr/listPreferredItemHeightSmall"
+    android:background="@color/transparent">
 
     <LinearLayout
         android:layout_width="wrap_content"
@@ -33,8 +34,8 @@
         <TextView android:id="@android:id/text1"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:textAppearance="?android:attr/textAppearanceListItem"
-            android:textColor="?android:attr/textColorAlertDialogListItem"
+            android:textAppearance="?attr/textAppearanceListItem"
+            android:textColor="?attr/textColorAlertDialogListItem"
             android:gravity="center_vertical|start"
             android:singleLine="true"
             android:ellipsize="marquee" />
@@ -42,8 +43,8 @@
         <TextView android:id="@android:id/text2"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:textAppearance="?android:attr/textAppearanceListItemSecondary"
-            android:textColor="?android:attr/textColorAlertDialogListItem"
+            android:textAppearance="?attr/textAppearanceListItemSecondary"
+            android:textColor="?attr/textColorAlertDialogListItem"
             android:gravity="center_vertical|start"
             android:singleLine="true"
             android:ellipsize="marquee" />
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 508a557..69440be 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -6267,13 +6267,22 @@
     </declare-styleable>
 
     <!-- Use <code>recognition-service</code> as the root tag of the XML resource that
-         describes a {@link android.speech.RecognitionService}, which is reference from
+         describes a {@link android.speech.RecognitionService}, which is referenced from
          its {@link android.speech.RecognitionService#SERVICE_META_DATA} meta-data entry.
          Described here are the attributes that can be included in that tag. -->
     <declare-styleable name="RecognitionService">
         <attr name="settingsActivity" />
     </declare-styleable>
 
+    <!-- Use <code>voice-interaction-service</code> as the root tag of the XML resource that
+         describes a {@link android.service.voice.VoiceInteractionService}, which is referenced from
+         its {@link android.service.voice.VoiceInteractionService#SERVICE_META_DATA} meta-data entry.
+         Described here are the attributes that can be included in that tag. -->
+    <declare-styleable name="VoiceInteractionService">
+        <attr name="sessionService" format="string" />
+        <attr name="settingsActivity" />
+    </declare-styleable>
+
     <!-- Attributes used to style the Action Bar. -->
     <declare-styleable name="ActionBar">
         <!-- The type of navigation to use. -->
diff --git a/core/res/res/values/dimens_quantum.xml b/core/res/res/values/dimens_quantum.xml
index 02e61e2..cebee12 100644
--- a/core/res/res/values/dimens_quantum.xml
+++ b/core/res/res/values/dimens_quantum.xml
@@ -47,4 +47,6 @@
     <dimen name="text_size_menu_quantum">14sp</dimen>
     <dimen name="text_size_button_quantum">14sp</dimen>
 
+    <dimen name="floating_window_z">16dp</dimen>
+    <dimen name="floating_window_margin">32dp</dimen>
 </resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 6491b33..085b9c3 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2155,6 +2155,13 @@
   <public type="attr" name="colorPrimaryDark" />
   <public type="attr" name="colorAccent" />
   <public type="attr" name="nestedScrollingEnabled" />
+  <public type="attr" name="windowEnterTransition" />
+  <public type="attr" name="windowExitTransition" />
+  <public type="attr" name="windowSharedElementEnterTransition" />
+  <public type="attr" name="windowSharedElementExitTransition" />
+  <public type="attr" name="windowAllowExitTransitionOverlap" />
+  <public type="attr" name="windowAllowEnterTransitionOverlap" />
+  <public type="attr" name="sessionService" />
 
   <public-padding type="dimen" name="l_resource_pad" end="0x01050010" />
 
@@ -2385,17 +2392,14 @@
 
   <public type="style" name="Widget.Holo.Light.Button.Borderless" />
 
+  <public-padding type="interpolator" name="l_resource_pad" end="0x010c0010" />
+
   <!-- An interpolator which accelerates fast but decelerates slowly. -->
   <public type="interpolator" name="fast_out_slow_in" />
   <!-- An interpolator which starts with a peak non-zero velocity and decelerates slowly. -->
   <public type="interpolator" name="linear_out_slow_in" />
   <!-- An interpolator which accelerates fast and keeps accelerating until the end. -->
   <public type="interpolator" name="fast_out_linear_in" />
-  <public type="attr" name="windowEnterTransition" />
-  <public type="attr" name="windowExitTransition" />
-  <public type="attr" name="windowSharedElementEnterTransition" />
-  <public type="attr" name="windowSharedElementExitTransition" />
-  <public type="attr" name="windowAllowExitTransitionOverlap" />
-  <public type="attr" name="windowAllowEnterTransitionOverlap" />
+
   <public type="transition" name="no_transition" id="0x010f0000"/>
 </resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 6d4ceef..cb52db2 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1068,6 +1068,12 @@
         interface of a wallpaper. Should never be needed for normal apps.</string>
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permlab_bindVoiceInteraction">bind to a voice interactor</string>
+    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permdesc_bindVoiceInteraction">Allows the holder to bind to the top-level
+        interface of a voice interaction service. Should never be needed for normal apps.</string>
+
+    <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_bindRemoteDisplay">bind to a remote display</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permdesc_bindRemoteDisplay">Allows the holder to bind to the top-level
@@ -2048,6 +2054,11 @@
     <string name="permdesc_bindNotificationListenerService">Allows the holder to bind to the top-level interface of a notification listener service. Should never be needed for normal apps.</string>
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permlab_bindConditionProviderService">bind to a condition provider service</string>
+    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permdesc_bindConditionProviderService">Allows the holder to bind to the top-level interface of a condition provider service. Should never be needed for normal apps.</string>
+
+    <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_invokeCarrierSetup">invoke the carrier-provided configuration app</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permdesc_invokeCarrierSetup">Allows the holder to invoke the carrier-provided configuration app. Should never be needed for normal apps.</string>
@@ -3797,6 +3808,8 @@
     <!-- Label to show for a service that is running because it is observing
          the user's notifications. -->
     <string name="notification_listener_binding_label">Notification listener</string>
+    <!-- Label to show for a service that is running because it is providing conditions. -->
+    <string name="condition_provider_service_binding_label">Condition provider</string>
 
     <!-- Do Not Translate: Alternate eri.xml -->
     <string name="alternate_eri_file">/data/eri.xml</string>
diff --git a/core/res/res/values/styles_quantum.xml b/core/res/res/values/styles_quantum.xml
index 0cbb655..57f2443 100644
--- a/core/res/res/values/styles_quantum.xml
+++ b/core/res/res/values/styles_quantum.xml
@@ -480,7 +480,7 @@
     <style name="Widget.Quantum.AbsListView" parent="Widget.AbsListView"/>
 
     <style name="Widget.Quantum.AutoCompleteTextView" parent="Widget.AutoCompleteTextView">
-        <item name="dropDownSelector">@drawable/list_selector_holo_dark</item>
+        <item name="dropDownSelector">@drawable/list_selector_quantum</item>
         <item name="popupBackground">?attr/colorBackground</item>
     </style>
 
@@ -654,7 +654,7 @@
 
     <style name="Widget.Quantum.Spinner" parent="Widget.Spinner.DropDown">
         <item name="background">@drawable/spinner_background_quantum</item>
-        <item name="dropDownSelector">@drawable/list_selector_holo_dark</item>
+        <item name="dropDownSelector">@drawable/list_selector_quantum</item>
         <item name="popupBackground">?attr/colorBackground</item>
         <item name="dropDownVerticalOffset">0dip</item>
         <item name="dropDownHorizontalOffset">0dip</item>
@@ -713,7 +713,7 @@
     <style name="Widget.Quantum.QuickContactBadgeSmall.WindowLarge" parent="Widget.QuickContactBadgeSmall.WindowLarge"/>
 
     <style name="Widget.Quantum.ListPopupWindow" parent="Widget.ListPopupWindow">
-        <item name="dropDownSelector">@drawable/list_selector_holo_dark</item>
+        <item name="dropDownSelector">@drawable/list_selector_quantum</item>
         <item name="popupBackground">?attr/colorBackground</item>
         <item name="dropDownVerticalOffset">0dip</item>
         <item name="dropDownHorizontalOffset">0dip</item>
@@ -851,7 +851,7 @@
     <style name="Widget.Quantum.Light.AbsListView" parent="Widget.Quantum.AbsListView"/>
 
     <style name="Widget.Quantum.Light.AutoCompleteTextView" parent="Widget.AutoCompleteTextView">
-        <item name="dropDownSelector">@drawable/list_selector_holo_light</item>
+        <item name="dropDownSelector">@drawable/list_selector_quantum</item>
         <item name="popupBackground">?attr/colorBackground</item>
     </style>
 
@@ -938,7 +938,7 @@
 
     <style name="Widget.Quantum.Light.Spinner" parent="Widget.Quantum.Spinner">
         <item name="background">@drawable/spinner_background_quantum</item>
-        <item name="dropDownSelector">@drawable/list_selector_holo_light</item>
+        <item name="dropDownSelector">@drawable/list_selector_quantum</item>
         <item name="popupBackground">?attr/colorBackground</item>
         <item name="dropDownVerticalOffset">0dip</item>
         <item name="dropDownHorizontalOffset">0dip</item>
@@ -962,7 +962,7 @@
     <style name="Widget.Quantum.Light.QuickContactBadgeSmall.WindowLarge" parent="Widget.Quantum.QuickContactBadgeSmall.WindowLarge"/>
 
     <style name="Widget.Quantum.Light.ListPopupWindow" parent="Widget.ListPopupWindow">
-        <item name="dropDownSelector">@drawable/list_selector_holo_light</item>
+        <item name="dropDownSelector">@drawable/list_selector_quantum</item>
         <item name="popupBackground">?attr/colorBackground</item>
         <item name="dropDownVerticalOffset">0dip</item>
         <item name="dropDownHorizontalOffset">0dip</item>
@@ -1019,16 +1019,16 @@
     <!-- Dialog styles -->
 
     <style name="AlertDialog.Quantum" parent="AlertDialog">
-        <item name="fullDark">?attr/colorBackground</item>
-        <item name="topDark">?attr/colorBackground</item>
-        <item name="centerDark">?attr/colorBackground</item>
-        <item name="bottomDark">?attr/colorBackground</item>
-        <item name="fullBright">?attr/colorBackground</item>
-        <item name="topBright">?attr/colorBackground</item>
-        <item name="centerBright">?attr/colorBackground</item>
-        <item name="bottomBright">?attr/colorBackground</item>
-        <item name="bottomMedium">?attr/colorBackground</item>
-        <item name="centerMedium">?attr/colorBackground</item>
+        <item name="fullDark">@color/transparent</item>
+        <item name="topDark">@color/transparent</item>
+        <item name="centerDark">@color/transparent</item>
+        <item name="bottomDark">@color/transparent</item>
+        <item name="fullBright">@color/transparent</item>
+        <item name="topBright">@color/transparent</item>
+        <item name="centerBright">@color/transparent</item>
+        <item name="bottomBright">@color/transparent</item>
+        <item name="bottomMedium">@color/transparent</item>
+        <item name="centerMedium">@color/transparent</item>
         <item name="layout">@layout/alert_dialog_quantum</item>
         <item name="listLayout">@layout/select_dialog_quantum</item>
         <item name="progressLayout">@layout/progress_dialog_quantum</item>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index bb0d184..431dab8 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1580,6 +1580,7 @@
   <java-symbol type="string" name="low_internal_storage_view_text" />
   <java-symbol type="string" name="low_internal_storage_view_title" />
   <java-symbol type="string" name="notification_listener_binding_label" />
+  <java-symbol type="string" name="condition_provider_service_binding_label" />
   <java-symbol type="string" name="report" />
   <java-symbol type="string" name="select_input_method" />
   <java-symbol type="string" name="select_keyboard_layout_notification_title" />
diff --git a/core/res/res/values/themes_quantum.xml b/core/res/res/values/themes_quantum.xml
index c2e31f4..9f76eae 100644
--- a/core/res/res/values/themes_quantum.xml
+++ b/core/res/res/values/themes_quantum.xml
@@ -125,7 +125,7 @@
         <item name="listChoiceIndicatorSingle">@drawable/btn_radio_quantum</item>
         <item name="listChoiceIndicatorMultiple">@drawable/btn_check_quantum_anim</item>
 
-        <item name="listChoiceBackgroundIndicator">@drawable/list_selector_holo_dark</item>
+        <item name="listChoiceBackgroundIndicator">@drawable/list_selector_quantum</item>
 
         <item name="activatedBackgroundIndicator">@drawable/activated_background_quantum</item>
 
@@ -468,7 +468,7 @@
         <item name="listChoiceIndicatorSingle">@drawable/btn_radio_quantum</item>
         <item name="listChoiceIndicatorMultiple">@drawable/btn_check_quantum_anim</item>
 
-        <item name="listChoiceBackgroundIndicator">@drawable/list_selector_holo_light</item>
+        <item name="listChoiceBackgroundIndicator">@drawable/list_selector_quantum</item>
 
         <item name="activatedBackgroundIndicator">@drawable/activated_background_quantum</item>
 
@@ -898,7 +898,7 @@
     <style name="Theme.Quantum.Dialog">
         <item name="windowFrame">@null</item>
         <item name="windowTitleStyle">@style/DialogWindowTitle.Quantum</item>
-        <item name="windowBackground">?attr/colorBackground</item>
+        <item name="windowBackground">@drawable/dialog_background_quantum</item>
         <item name="windowIsFloating">true</item>
         <item name="windowContentOverlay">@null</item>
         <item name="windowAnimationStyle">@style/Animation.Quantum.Dialog</item>
@@ -964,8 +964,6 @@
          its pixels. -->
     <style name="Theme.Quantum.Dialog.NoFrame">
         <item name="windowBackground">@color/transparent</item>
-        <item name="windowFrame">@null</item>
-        <item name="windowContentOverlay">@null</item>
         <item name="windowAnimationStyle">@null</item>
         <item name="backgroundDimEnabled">false</item>
         <item name="windowIsTranslucent">true</item>
@@ -981,7 +979,6 @@
     <style name="Theme.Quantum.Dialog.Alert">
         <item name="windowBackground">@color/transparent</item>
         <item name="windowTitleStyle">@style/DialogWindowTitle.Quantum</item>
-        <item name="windowContentOverlay">@null</item>
         <item name="windowMinWidthMajor">@dimen/dialog_min_width_major</item>
         <item name="windowMinWidthMinor">@dimen/dialog_min_width_minor</item>
     </style>
@@ -1102,7 +1099,6 @@
     <style name="Theme.Quantum.Light.Dialog.Alert">
         <item name="windowBackground">@color/transparent</item>
         <item name="windowTitleStyle">@style/DialogWindowTitle.Quantum.Light</item>
-        <item name="windowContentOverlay">@null</item>
         <item name="windowMinWidthMajor">@dimen/dialog_min_width_major</item>
         <item name="windowMinWidthMinor">@dimen/dialog_min_width_minor</item>
     </style>
@@ -1112,7 +1108,6 @@
     <style name="Theme.Quantum.Light.Dialog.TimePicker">
         <item name="windowBackground">@color/transparent</item>
         <item name="windowTitleStyle">@style/DialogWindowTitle.Quantum.Light</item>
-        <item name="windowContentOverlay">@null</item>
     </style>
 
     <!-- Theme for a presentation window on a secondary display. -->
diff --git a/docs/html/sdk/installing/installing-adt.jd b/docs/html/sdk/installing/installing-adt.jd
index e4cf1bc..e12dfd8 100644
--- a/docs/html/sdk/installing/installing-adt.jd
+++ b/docs/html/sdk/installing/installing-adt.jd
@@ -1,8 +1,8 @@
 page.title=Installing the Eclipse Plugin
-adt.zip.version=22.6.2
-adt.zip.download=ADT-22.6.2.zip
-adt.zip.bytes=14586842
-adt.zip.checksum=f660959fa71262b4285bcb64be284bf5
+adt.zip.version=22.6.3
+adt.zip.download=ADT-22.6.3.zip
+adt.zip.bytes=14590813
+adt.zip.checksum=3982259fd2cc81e53bbbe05dcd6529a7
 
 @jd:body
 
diff --git a/docs/html/tools/sdk/eclipse-adt.jd b/docs/html/tools/sdk/eclipse-adt.jd
index d106f4a..124e58d 100644
--- a/docs/html/tools/sdk/eclipse-adt.jd
+++ b/docs/html/tools/sdk/eclipse-adt.jd
@@ -53,10 +53,49 @@
 <p>For a summary of all known issues in ADT, see <a
 href="http://tools.android.com/knownissues">http://tools.android.com/knownissues</a>.</p>
 
-
 <div class="toggle-content opened">
   <p><a href="#" onclick="return toggleContent(this)">
     <img src="{@docRoot}assets/images/triangle-opened.png" class="toggle-content-img"
+      alt=""/>ADT 22.6.3</a> <em>(April 2014)</em>
+  </p>
+
+  <div class="toggle-content-toggleme">
+<dl>
+  <dt>Dependencies:</dt>
+
+  <dd>
+    <ul>
+      <li>Java 1.6 or higher is required.</li>
+      <li>Eclipse Indigo (Version 3.7.2) or higher is required.</li>
+      <li>This version of ADT is designed for use with
+        <a href="{@docRoot}tools/sdk/tools-notes.html">SDK Tools r22.6.3</a>.
+        If you haven't already installed SDK Tools r22.6.3 into your SDK, use the
+        Android SDK Manager to do so.</li>
+    </ul>
+  </dd>
+
+  <dt>General Notes:</dt>
+  <dd>
+    <ul>
+      <li>Fixed a problem where the AVD manager allowed creating Android Wear virtual devices
+          with a target API Level lower than 19.</li>
+      <li>Fixed the description of Android Wear system images in the SDK Manager.</li>
+    </ul>
+  </dd>
+
+  <dt>Known Issues:</dt>
+  <dd>
+    <p>When you create an Android Wear virtual device in the AVD manager, a target API Level
+       lower than 19 may be selected by default. Make sure you select the target API Level 19
+       when creating Android Wear virtual devices.</p>
+  </dd>
+</dl>
+</div>
+</div>
+
+<div class="toggle-content closed">
+  <p><a href="#" onclick="return toggleContent(this)">
+    <img src="{@docRoot}assets/images/triangle-closed.png" class="toggle-content-img"
       alt=""/>ADT 22.6.2</a> <em>(March 2014)</em>
   </p>
 
diff --git a/docs/html/tools/sdk/ndk/index.jd b/docs/html/tools/sdk/ndk/index.jd
index a22dc90..0ac1881 100644
--- a/docs/html/tools/sdk/ndk/index.jd
+++ b/docs/html/tools/sdk/ndk/index.jd
@@ -245,8 +245,8 @@
   but it always increases your app complexity. In general, you should only use the NDK
   if it is essential to your app&mdash;never because you simply prefer to program in C/C++.</p>
 
-  <p>Typical good candidates for the NDK are self-contained, CPU-intensive operations that don't
-  allocate much memory, such as signal processing, physics simulation, and so on. When examining
+  <p>Typical good candidates for the NDK are CPU-intensive workloads such as game engines,
+  signal processing, physics simulation, and so on. When examining
   whether or not you should develop in native code, think about your requirements and see if the
   Android framework APIs provide the functionality that you need.</p>
 
diff --git a/docs/html/tools/sdk/tools-notes.jd b/docs/html/tools/sdk/tools-notes.jd
index 14b5505..9b06a9d 100644
--- a/docs/html/tools/sdk/tools-notes.jd
+++ b/docs/html/tools/sdk/tools-notes.jd
@@ -28,6 +28,46 @@
 <div class="toggle-content opened">
   <p><a href="#" onclick="return toggleContent(this)">
     <img src="{@docRoot}assets/images/triangle-opened.png" class="toggle-content-img"
+      alt=""/>SDK Tools, Revision 22.6.3</a> <em>(April 2014)</em>
+  </p>
+
+  <div class="toggle-content-toggleme">
+
+    <dl>
+    <dt>Dependencies:</dt>
+
+    <dd>
+      <ul>
+        <li>Android SDK Platform-tools revision 18 or later.</li>
+        <li>If you are developing in Eclipse with ADT, note that this version of SDK Tools is
+          designed for use with ADT 22.6.3 and later. If you haven't already, update your
+        <a href="{@docRoot}tools/sdk/eclipse-adt.html">ADT Plugin</a> to 22.6.3.</li>
+        <li>If you are developing outside Eclipse, you must have
+          <a href="http://ant.apache.org/">Apache Ant</a> 1.8 or later.</li>
+      </ul>
+    </dd>
+
+    <dt>General Notes:</dt>
+    <dd>
+      <ul>
+        <li>Fixed a problem where the AVD manager allowed creating Android Wear virtual devices
+            with a target API Level lower than 19.</li>
+        <li>Fixed the description of Android Wear system images in the SDK Manager.</li>
+      </ul>
+    </dd>
+
+    <dt>Known Issues:</dt>
+    <dd>
+      <p>When you create an Android Wear virtual device in the AVD manager, a target API Level
+         lower than 19 may be selected by default. Make sure you select the target API Level 19
+         when creating Android Wear virtual devices.</p>
+    </dd>
+  </div>
+</div>
+
+<div class="toggle-content closed">
+  <p><a href="#" onclick="return toggleContent(this)">
+    <img src="{@docRoot}assets/images/triangle-closed.png" class="toggle-content-img"
       alt=""/>SDK Tools, Revision 22.6.2</a> <em>(March 2014)</em>
   </p>
 
diff --git a/graphics/java/android/graphics/Outline.java b/graphics/java/android/graphics/Outline.java
index 4ea7ec7..b5c0801 100644
--- a/graphics/java/android/graphics/Outline.java
+++ b/graphics/java/android/graphics/Outline.java
@@ -16,18 +16,21 @@
 
 package android.graphics;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.graphics.drawable.Drawable;
 import android.view.View;
 
 /**
- * Defines an area of content.
+ * Defines a simple shape, used for bounding graphical regions.
  *
- * Can be used with a View or Drawable to drive the shape of shadows cast by a
- * View, and allowing Views to clip inner content.
+ * Can be used with a View, or computed by a Drawable, to drive the shape of shadows cast by a
+ * View.
  *
  * @see View#setOutline(Outline)
- * @see View#setClipToOutline(boolean)
+ * @see Drawable#getOutline(Outline)
  */
-public class Outline {
+public final class Outline {
     /** @hide */
     public Rect mRect;
 
@@ -44,46 +47,74 @@
     public Outline() {}
 
     /**
+     * Constructs an Outline with a copy of the data in src.
+     */
+    public Outline(@Nullable Outline src) {
+        set(src);
+    }
+
+    /** @hide */
+    public void markInvalid() {
+        mRadius = 0;
+        mRect = null;
+        mPath = null;
+    }
+
+    /**
      * Returns whether the Outline is valid for use with a View.
      * <p>
      * Outlines are invalid when constructed until a setter method is called.
      */
-    public final boolean isValid() {
+    public boolean isValid() {
         return mRect != null || mPath != null;
     }
 
     /**
-     * @hide
-     */
-    public final boolean canClip() {
-        return mPath == null;
-    }
-
-    /**
      * Replace the contents of this Outline with the contents of src.
+     *
+     * @param src Source outline to copy from.
      */
-    public void set(Outline src) {
-        if (src.mPath != null) {
-            if (mPath == null) {
-                mPath = new Path();
-            }
-            mPath.set(src.mPath);
+    public void set(@Nullable Outline src) {
+        if (src == null) {
+            mRadius = 0;
             mRect = null;
-        }
-        if (src.mRect != null) {
-            if (mRect == null) {
-                mRect = new Rect();
+            mPath = null;
+        } else {
+            if (src.mPath != null) {
+                if (mPath == null) {
+                    mPath = new Path();
+                }
+                mPath.set(src.mPath);
+                mRect = null;
             }
-            mRect.set(src.mRect);
+            if (src.mRect != null) {
+                if (mRect == null) {
+                    mRect = new Rect();
+                }
+                mRect.set(src.mRect);
+            }
+            mRadius = src.mRadius;
         }
-        mRadius = src.mRadius;
     }
 
     /**
      * Sets the Outline to the rounded rect defined by the input rect, and corner radius.
-     * <p>
-     * Outlines produced by this method support
-     * {@link View#setClipToOutline(boolean) View clipping.}
+     */
+    public void setRect(int left, int top, int right, int bottom) {
+        setRoundRect(left, top, right, bottom, 0.0f);
+    }
+
+    /**
+     * Convenience for {@link #setRect(int, int, int, int)}
+     */
+    public void setRect(@NonNull Rect rect) {
+        setRect(rect.left, rect.top, rect.right, rect.bottom);
+    }
+
+    /**
+     * Sets the Outline to the rounded rect defined by the input rect, and corner radius.
+     *
+     * Passing a zero radius is equivalent to calling {@link #setRect(int, int, int, int)}
      */
     public void setRoundRect(int left, int top, int right, int bottom, float radius) {
         if (mRect == null) mRect = new Rect();
@@ -93,11 +124,33 @@
     }
 
     /**
-     * Sets the Constructs an Outline from a {@link android.graphics.Path#isConvex() convex path}.
-     *
-     * @hide
+     * Convenience for {@link #setRoundRect(int, int, int, int, float)}
      */
-    public void setConvexPath(Path convexPath) {
+    public void setRoundRect(@NonNull Rect rect, float radius) {
+        setRoundRect(rect.left, rect.top, rect.right, rect.bottom, radius);
+    }
+
+    /**
+     * Sets the outline to the oval defined by input rect.
+     */
+    public void setOval(int left, int top, int right, int bottom) {
+        mRect = null;
+        if (mPath == null) mPath = new Path();
+        mPath.reset();
+        mPath.addOval(left, top, right, bottom, Path.Direction.CW);
+    }
+
+    /**
+     * Convenience for {@link #setOval(int, int, int, int)}
+     */
+    public void setOval(@NonNull Rect rect) {
+        setOval(rect.left, rect.top, rect.right, rect.bottom);
+    }
+
+    /**
+     * Sets the Constructs an Outline from a {@link android.graphics.Path#isConvex() convex path}.
+     */
+    public void setConvexPath(@NonNull Path convexPath) {
         if (!convexPath.isConvex()) {
             throw new IllegalArgumentException("path must be convex");
         }
diff --git a/graphics/java/android/graphics/Path.java b/graphics/java/android/graphics/Path.java
index c07a6da..c600f47 100644
--- a/graphics/java/android/graphics/Path.java
+++ b/graphics/java/android/graphics/Path.java
@@ -499,11 +499,7 @@
      * @param dir  The direction to wind the rectangle's contour
      */
     public void addRect(RectF rect, Direction dir) {
-        if (rect == null) {
-            throw new NullPointerException("need rect parameter");
-        }
-        detectSimplePath(rect.left, rect.top, rect.right, rect.bottom, dir);
-        native_addRect(mNativePath, rect, dir.nativeInt);
+        addRect(rect.left, rect.top, rect.right, rect.bottom, dir);
     }
 
     /**
@@ -527,11 +523,17 @@
      * @param dir  The direction to wind the oval's contour
      */
     public void addOval(RectF oval, Direction dir) {
-        if (oval == null) {
-            throw new NullPointerException("need oval parameter");
-        }
+        addOval(oval.left, oval.top, oval.right, oval.bottom, dir);
+    }
+
+    /**
+     * Add a closed oval contour to the path
+     *
+     * @param dir The direction to wind the oval's contour
+     */
+    public void addOval(float left, float top, float right, float bottom, Direction dir) {
         isSimplePath = false;
-        native_addOval(mNativePath, oval, dir.nativeInt);
+        native_addOval(mNativePath, left, top, right, bottom, dir.nativeInt);
     }
 
     /**
@@ -756,10 +758,10 @@
     private static native void native_arcTo(long nPath, RectF oval,
                     float startAngle, float sweepAngle, boolean forceMoveTo);
     private static native void native_close(long nPath);
-    private static native void native_addRect(long nPath, RectF rect, int dir);
     private static native void native_addRect(long nPath, float left, float top,
                                             float right, float bottom, int dir);
-    private static native void native_addOval(long nPath, RectF oval, int dir);
+    private static native void native_addOval(long nPath, float left, float top,
+            float right, float bottom, int dir);
     private static native void native_addCircle(long nPath, float x, float y, float radius, int dir);
     private static native void native_addArc(long nPath, RectF oval,
                                             float startAngle, float sweepAngle);
diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java
index 74d1219..b9d5e19 100644
--- a/graphics/java/android/graphics/drawable/Drawable.java
+++ b/graphics/java/android/graphics/drawable/Drawable.java
@@ -864,22 +864,21 @@
     }
 
     /**
-     * Returns the outline for this drawable if defined, null if not.
+     * Called to get the drawable to populate the Outline.
      * <p>
-     * This method will be called by a View on its background Drawable after
-     * bounds change, if the View's Outline isn't set explicitly. This allows
-     * the background Drawable to provide the shape of the shadow casting
-     * portion of the View. It can also serve to clip the area of the View if
-     * if {@link View#setClipToOutline(boolean)} is set on the View.
-     * <p>
-     * The Outline queried by the View will not be modified, and is treated as
-     * a static shape that only needs to be requeried when the drawable's bounds
-     * change.
+     * This method will be called by a View on its background Drawable after bounds change, or its
+     * Drawable is invalidated, if the View's Outline isn't set explicitly. This allows the
+     * background Drawable to define the shape of the shadow cast by the View.
      *
-     * @see View#setOutline(android.view.Outline)
-     * @see View#setClipToOutline(boolean)
+     * The default behavior defines the outline to be the bounding rectangle. Subclasses that wish
+     * to convey a different shape must override this method.
+     *
+     * @see View#setOutline(android.graphics.Outline)
      */
-    public Outline getOutline() { return null; }
+    public boolean getOutline(Outline outline) {
+        outline.setRect(getBounds());
+        return true;
+    }
 
     /**
      * Make this drawable mutable. This operation cannot be reversed. A mutable
diff --git a/graphics/java/android/graphics/drawable/GradientDrawable.java b/graphics/java/android/graphics/drawable/GradientDrawable.java
index 1b5cefe..dc06350 100644
--- a/graphics/java/android/graphics/drawable/GradientDrawable.java
+++ b/graphics/java/android/graphics/drawable/GradientDrawable.java
@@ -139,8 +139,7 @@
 
     private final Path mPath = new Path();
     private final RectF mRect = new RectF();
-    private Outline mOutline;
-    
+
     private Paint mLayerPaint;    // internal, used if we use saveLayer()
     private boolean mRectIsDirty;   // internal state
     private boolean mMutated;
@@ -573,15 +572,11 @@
                 mStrokePaint.setColorFilter(mColorFilter);
             }
         }
-        
+
         switch (st.mShape) {
             case RECTANGLE:
                 if (st.mRadiusArray != null) {
-                    if (mPathIsDirty || mRectIsDirty) {
-                        mPath.reset();
-                        mPath.addRoundRect(mRect, st.mRadiusArray, Path.Direction.CW);
-                        mPathIsDirty = mRectIsDirty = false;
-                    }
+                    buildPathIfDirty();
                     canvas.drawPath(mPath, mFillPaint);
                     if (haveStroke) {
                         canvas.drawPath(mPath, mStrokePaint);
@@ -638,7 +633,16 @@
             }
         }
     }
-    
+
+    private void buildPathIfDirty() {
+        final GradientState st = mGradientState;
+        if (mPathIsDirty || mRectIsDirty) {
+            mPath.reset();
+            mPath.addRoundRect(mRect, st.mRadiusArray, Path.Direction.CW);
+            mPathIsDirty = mRectIsDirty = false;
+        }
+    }
+
     private Path buildRing(GradientState st) {
         if (mRingPath != null && (!st.mUseLevelForShape || !mPathIsDirty)) return mRingPath;
         mPathIsDirty = false;
@@ -862,7 +866,7 @@
                 float x0, x1, y0, y1;
 
                 if (st.mGradient == LINEAR_GRADIENT) {
-                    final float level = st.mUseLevel ? getLevel() / 10000.0f : 1.0f;                    
+                    final float level = st.mUseLevel ? getLevel() / 10000.0f : 1.0f;
                     switch (st.mOrientation) {
                     case TOP_BOTTOM:
                         x0 = r.left;            y0 = r.top;
@@ -1428,42 +1432,40 @@
     }
 
     @Override
-    public Outline getOutline() {
+    public boolean getOutline(Outline outline) {
         final GradientState st = mGradientState;
         final Rect bounds = getBounds();
 
         switch (st.mShape) {
             case RECTANGLE:
                 if (st.mRadiusArray != null) {
-                    return null;
+                    buildPathIfDirty();
+                    outline.setConvexPath(mPath);
+                    return true;
                 }
+
                 float rad = 0;
                 if (st.mRadius > 0.0f) {
                     // clamp the radius based on width & height, matching behavior in draw()
                     rad = Math.min(st.mRadius,
                             Math.min(bounds.width(), bounds.height()) * 0.5f);
                 }
-                if (mOutline == null) {
-                    mOutline = new Outline();
-                }
-                mOutline.setRoundRect(bounds.left, bounds.top,
-                        bounds.right, bounds.bottom, rad);
-                return mOutline;
-            case LINE: {
+                outline.setRoundRect(bounds, rad);
+                return true;
+            case OVAL:
+                outline.setOval(bounds);
+                return true;
+            case LINE:
                 float halfStrokeWidth = mStrokePaint.getStrokeWidth() * 0.5f;
                 float centerY = bounds.centerY();
                 int top = (int) Math.floor(centerY - halfStrokeWidth);
                 int bottom = (int) Math.ceil(centerY + halfStrokeWidth);
 
-                if (mOutline == null) {
-                    mOutline = new Outline();
-                }
-                mOutline.setRoundRect(bounds.left, top, bounds.right, bottom, 0);
-                return mOutline;
-            }
+                outline.setRect(bounds.left, top, bounds.right, bottom);
+                return true;
             default:
                 // TODO: investigate
-                return null;
+                return false;
         }
     }
 
diff --git a/graphics/java/android/graphics/drawable/ShapeDrawable.java b/graphics/java/android/graphics/drawable/ShapeDrawable.java
index 96309f9..61b1b85 100644
--- a/graphics/java/android/graphics/drawable/ShapeDrawable.java
+++ b/graphics/java/android/graphics/drawable/ShapeDrawable.java
@@ -21,6 +21,7 @@
 import android.content.res.TypedArray;
 import android.graphics.Canvas;
 import android.graphics.ColorFilter;
+import android.graphics.Outline;
 import android.graphics.Paint;
 import android.graphics.PixelFormat;
 import android.graphics.PorterDuff.Mode;
@@ -496,6 +497,16 @@
     }
 
     @Override
+    public boolean getOutline(Outline outline) {
+        if (mShapeState.mShape == null) {
+            // don't publish outline without a shape
+            return false;
+        }
+
+        return mShapeState.mShape.getOutline(outline);
+    }
+
+    @Override
     public ConstantState getConstantState() {
         mShapeState.mChangingConfigurations = getChangingConfigurations();
         return mShapeState;
diff --git a/graphics/java/android/graphics/drawable/TouchFeedbackDrawable.java b/graphics/java/android/graphics/drawable/TouchFeedbackDrawable.java
index 3b9d513..824e108 100644
--- a/graphics/java/android/graphics/drawable/TouchFeedbackDrawable.java
+++ b/graphics/java/android/graphics/drawable/TouchFeedbackDrawable.java
@@ -48,6 +48,7 @@
 public class TouchFeedbackDrawable extends LayerDrawable {
     private static final String LOG_TAG = TouchFeedbackDrawable.class.getSimpleName();
     private static final PorterDuffXfermode DST_IN = new PorterDuffXfermode(Mode.DST_IN);
+    private static final PorterDuffXfermode SRC_OVER = new PorterDuffXfermode(Mode.SRC_OVER);
 
     /** The maximum number of ripples supported. */
     private static final int MAX_RIPPLES = 10;
@@ -105,6 +106,26 @@
     protected boolean onStateChange(int[] stateSet) {
         super.onStateChange(stateSet);
 
+        // TODO: Implicitly tie states to ripple IDs. For now, just clear
+        // focused and pressed if they aren't in the state set.
+        boolean hasFocused = false;
+        boolean hasPressed = false;
+        for (int i = 0; i < stateSet.length; i++) {
+            if (stateSet[i] == R.attr.state_pressed) {
+                hasPressed = true;
+            } else if (stateSet[i] == R.attr.state_focused) {
+                hasFocused = true;
+            }
+        }
+
+        if (!hasPressed) {
+            removeHotspot(R.attr.state_pressed);
+        }
+
+        if (!hasFocused) {
+            removeHotspot(R.attr.state_focused);
+        }
+
         if (mRipplePaint != null && mState.mTint != null) {
             final ColorStateList stateList = mState.mTint;
             final int newColor = stateList.getColorForState(stateSet, 0);
@@ -122,7 +143,7 @@
     @Override
     protected void onBoundsChange(Rect bounds) {
         super.onBoundsChange(bounds);
-        
+
         if (!mOverrideBounds) {
             mHotspotBounds.set(bounds);
         }
@@ -217,6 +238,27 @@
         super.inflate(r, parser, attrs, theme);
 
         setTargetDensity(r.getDisplayMetrics());
+
+        // Find the mask
+        final int N = getNumberOfLayers();
+        for (int i = 0; i < N; i++) {
+            if (mLayerState.mChildren[i].mId == R.id.mask) {
+                mState.mMask = mLayerState.mChildren[i].mDrawable;
+            }
+        }
+    }
+
+    @Override
+    public boolean setDrawableByLayerId(int id, Drawable drawable) {
+        if (super.setDrawableByLayerId(id, drawable)) {
+            if (id == R.id.mask) {
+                mState.mMask = drawable;
+            }
+
+            return true;
+        }
+
+        return false;
     }
 
     /**
@@ -310,7 +352,7 @@
             mTouchedRipples = new SparseArray<Ripple>();
             mActiveRipples = new Ripple[MAX_RIPPLES];
         }
-        
+
         if (mActiveRipplesCount >= MAX_RIPPLES) {
             Log.e(LOG_TAG, "Max ripple count exceeded", new RuntimeException());
             return;
@@ -415,99 +457,139 @@
 
     @Override
     public void draw(Canvas canvas) {
-        final boolean projected = getNumberOfLayers() == 0;
+        final int N = mLayerState.mNum;
+        final Rect bounds = getBounds();
+        final ChildDrawable[] array = mLayerState.mChildren;
+        final boolean maskOnly = mState.mMask != null && N == 1;
+
+        int restoreToCount = drawRippleLayer(canvas, bounds, maskOnly);
+
+        if (restoreToCount >= 0) { 
+            // We have a ripple layer that contains ripples. If we also have an
+            // explicit mask drawable, apply it now using DST_IN blending.
+            if (mState.mMask != null) {
+                canvas.saveLayer(bounds.left, bounds.top, bounds.right,
+                        bounds.bottom, getMaskingPaint(DST_IN));
+                mState.mMask.draw(canvas);
+                canvas.restoreToCount(restoreToCount);
+                restoreToCount = -1;
+            }
+
+            // If there's more content, we need an extra masking layer to merge
+            // the ripples over the content.
+            if (!maskOnly) {
+                final PorterDuffXfermode xfermode = mState.getTintXfermodeInverse();
+                final int count = canvas.saveLayer(bounds.left, bounds.top,
+                        bounds.right, bounds.bottom, getMaskingPaint(xfermode));
+                if (restoreToCount < 0) {
+                    restoreToCount = count;
+                }
+            }
+        }
+
+        // Draw everything except the mask.
+        for (int i = 0; i < N; i++) {
+            if (array[i].mId != R.id.mask) {
+                array[i].mDrawable.draw(canvas);
+            }
+        }
+
+        // Composite the layers if needed.
+        if (restoreToCount >= 0) {
+            canvas.restoreToCount(restoreToCount);
+        }
+    }
+
+    private int drawRippleLayer(Canvas canvas, Rect bounds, boolean maskOnly) {
         final Ripple[] activeRipples = mActiveRipples;
         final int ripplesCount = mActiveRipplesCount;
-        final Rect bounds = getBounds();
+
+        Paint ripplePaint = null;
+        boolean drewRipples = false;
+        int restoreToCount = -1;
+        int activeRipplesCount = 0;
 
         // Draw ripples.
-        boolean drewRipples = false;
-        int rippleRestoreCount = -1;
-        int activeRipplesCount = 0;
         for (int i = 0; i < ripplesCount; i++) {
             final Ripple ripple = activeRipples[i];
             final RippleAnimator animator = ripple.animate();
             animator.update();
+
+            // Mark and skip inactive ripples.
             if (!animator.isRunning()) {
                 activeRipples[i] = null;
-            } else {
-                // If we're masking the ripple layer, make sure we have a layer
-                // first. This will merge SRC_OVER (directly) onto the canvas.
-                if (!projected && rippleRestoreCount < 0) {
-                    rippleRestoreCount = canvas.saveLayer(bounds.left, bounds.top,
-                            bounds.right, bounds.bottom, null);
+                continue;
+            }
+
+            // If we're masking the ripple layer, make sure we have a layer
+            // first. This will merge SRC_OVER (directly) onto the canvas.
+            if (restoreToCount < 0) {
+                // Separate the ripple color and alpha channel. The alpha will be
+                // applied when we merge the ripples down to the canvas.
+                final int rippleColor;
+                if (mState.mTint != null) {
+                    rippleColor = mState.mTint.getColorForState(getState(), Color.TRANSPARENT);
+                } else {
+                    rippleColor = Color.TRANSPARENT;
+                }
+                final int rippleAlpha = Color.alpha(rippleColor);
+
+                // If we're projecting or we only have a mask, we want to treat the
+                // underlying canvas as our content and merge the ripple layer down
+                // using the tint xfermode.
+                final boolean projected = isProjected();
+                final PorterDuffXfermode xfermode;
+                if (projected || maskOnly) {
+                    xfermode = mState.getTintXfermode();
+                } else {
+                    xfermode = SRC_OVER;
                 }
 
-                drewRipples |= ripple.draw(canvas, getRipplePaint());
-
-                activeRipples[activeRipplesCount] = activeRipples[i];
-                activeRipplesCount++;
+                final Paint layerPaint = getMaskingPaint(xfermode);
+                layerPaint.setAlpha(rippleAlpha);
+                final Rect layerBounds = projected ? getDirtyBounds() : bounds;
+                restoreToCount = canvas.saveLayer(layerBounds.left, layerBounds.top,
+                        layerBounds.right, layerBounds.bottom, layerPaint);
+                layerPaint.setAlpha(255);
             }
+
+            if (mRipplePaint == null) {
+                mRipplePaint = new Paint();
+                mRipplePaint.setAntiAlias(true);
+            }
+
+            drewRipples |= ripple.draw(canvas, mRipplePaint);
+
+            activeRipples[activeRipplesCount] = activeRipples[i];
+            activeRipplesCount++;
         }
+
         mActiveRipplesCount = activeRipplesCount;
 
-        // TODO: Use the masking layer first, if there is one.
-
-        // If we have ripples and content, we need a masking layer. This will
-        // merge DST_ATOP onto (effectively under) the ripple layer.
-        if (drewRipples && !projected && rippleRestoreCount >= 0) {
-            final PorterDuffXfermode xfermode = mState.getTintXfermode();
-            canvas.saveLayer(bounds.left, bounds.top,
-                    bounds.right, bounds.bottom, getMaskingPaint(xfermode));
+        // If we created a layer with no content, merge it immediately.
+        if (restoreToCount >= 0 && !drewRipples) {
+            canvas.restoreToCount(restoreToCount);
+            restoreToCount = -1;
         }
 
-        Drawable mask = null;
-        final ChildDrawable[] array = mLayerState.mChildren;
-        final int N = mLayerState.mNum;
-        for (int i = 0; i < N; i++) {
-            if (array[i].mId != R.id.mask) {
-                array[i].mDrawable.draw(canvas);
-            } else {
-                mask = array[i].mDrawable;
-            }
-        }
-
-        // If we have ripples, mask them.
-        if (mask != null && drewRipples) {
-            // TODO: This will also mask the lower layer, which is bad.
-            canvas.saveLayer(bounds.left, bounds.top, bounds.right,
-                    bounds.bottom, getMaskingPaint(DST_IN));
-            mask.draw(canvas);
-        }
-
-        // Composite the layers if needed.
-        if (rippleRestoreCount >= 0) {
-            canvas.restoreToCount(rippleRestoreCount);
-        }
+        return restoreToCount;
     }
 
-    private Paint getRipplePaint() {
-        if (mRipplePaint == null) {
-            mRipplePaint = new Paint();
-            mRipplePaint.setAntiAlias(true);
-
-            if (mState.mTint != null) {
-                final int color = mState.mTint.getColorForState(getState(), Color.TRANSPARENT);
-                mRipplePaint.setColor(color);
-            }
-        }
-        return mRipplePaint;
-    }
-
-    private Paint getMaskingPaint(PorterDuffXfermode mode) {
+    private Paint getMaskingPaint(PorterDuffXfermode xfermode) {
         if (mMaskingPaint == null) {
             mMaskingPaint = new Paint();
         }
-        mMaskingPaint.setXfermode(mode);
+        mMaskingPaint.setXfermode(xfermode);
         return mMaskingPaint;
     }
 
     @Override
     public Rect getDirtyBounds() {
-        final Rect dirtyBounds = mDirtyBounds;
         final Rect drawingBounds = mDrawingBounds;
+        final Rect dirtyBounds = mDirtyBounds;
         dirtyBounds.set(drawingBounds);
         drawingBounds.setEmpty();
+
         final Rect rippleBounds = mTempRect;
         final Ripple[] activeRipples = mActiveRipples;
         final int N = mActiveRipplesCount;
@@ -530,6 +612,8 @@
         int[] mTouchThemeAttrs;
         ColorStateList mTint;
         PorterDuffXfermode mTintXfermode;
+        PorterDuffXfermode mTintXfermodeInverse;
+        Drawable mMask;
         boolean mPinned;
 
         public TouchFeedbackState(
@@ -540,19 +624,26 @@
                 mTouchThemeAttrs = orig.mTouchThemeAttrs;
                 mTint = orig.mTint;
                 mTintXfermode = orig.mTintXfermode;
+                mTintXfermodeInverse = orig.mTintXfermodeInverse;
                 mPinned = orig.mPinned;
+                mMask = orig.mMask;
             }
         }
-        
+
         public void setTintMode(Mode mode) {
             final Mode invertedMode = TouchFeedbackState.invertPorterDuffMode(mode);
-            mTintXfermode = new PorterDuffXfermode(invertedMode);
+            mTintXfermodeInverse = new PorterDuffXfermode(invertedMode);
+            mTintXfermode = new PorterDuffXfermode(mode);
         }
-        
+
         public PorterDuffXfermode getTintXfermode() {
             return mTintXfermode;
         }
 
+        public PorterDuffXfermode getTintXfermodeInverse() {
+            return mTintXfermodeInverse;
+        }
+
         @Override
         public boolean canApplyTheme() {
             return mTouchThemeAttrs != null || super.canApplyTheme();
diff --git a/graphics/java/android/graphics/drawable/shapes/OvalShape.java b/graphics/java/android/graphics/drawable/shapes/OvalShape.java
index c914999..198dcc1 100644
--- a/graphics/java/android/graphics/drawable/shapes/OvalShape.java
+++ b/graphics/java/android/graphics/drawable/shapes/OvalShape.java
@@ -17,7 +17,9 @@
 package android.graphics.drawable.shapes;
 
 import android.graphics.Canvas;
+import android.graphics.Outline;
 import android.graphics.Paint;
+import android.graphics.RectF;
 
 /**
  * Defines an oval shape. 
@@ -36,5 +38,13 @@
     public void draw(Canvas canvas, Paint paint) {
         canvas.drawOval(rect(), paint);
     }
+
+    @Override
+    public boolean getOutline(Outline outline) {
+        final RectF rect = rect();
+        outline.setOval((int) Math.ceil(rect.left), (int) Math.ceil(rect.top),
+                (int) Math.floor(rect.right), (int) Math.floor(rect.bottom));
+        return true;
+    }
 }
 
diff --git a/graphics/java/android/graphics/drawable/shapes/RectShape.java b/graphics/java/android/graphics/drawable/shapes/RectShape.java
index a3d2654..2a0256c 100644
--- a/graphics/java/android/graphics/drawable/shapes/RectShape.java
+++ b/graphics/java/android/graphics/drawable/shapes/RectShape.java
@@ -17,6 +17,7 @@
 package android.graphics.drawable.shapes;
 
 import android.graphics.Canvas;
+import android.graphics.Outline;
 import android.graphics.Paint;
 import android.graphics.RectF;
 
@@ -40,10 +41,18 @@
     }
 
     @Override
+    public boolean getOutline(Outline outline) {
+        final RectF rect = rect();
+        outline.setRect((int) Math.ceil(rect.left), (int) Math.ceil(rect.top),
+                (int) Math.floor(rect.right), (int) Math.floor(rect.bottom));
+        return true;
+    }
+
+    @Override
     protected void onResize(float width, float height) {
         mRect.set(0, 0, width, height);
     }
-    
+
     /**
      * Returns the RectF that defines this rectangle's bounds.
      */
diff --git a/graphics/java/android/graphics/drawable/shapes/RoundRectShape.java b/graphics/java/android/graphics/drawable/shapes/RoundRectShape.java
index b469d2a..a6bb1bb 100644
--- a/graphics/java/android/graphics/drawable/shapes/RoundRectShape.java
+++ b/graphics/java/android/graphics/drawable/shapes/RoundRectShape.java
@@ -17,6 +17,7 @@
 package android.graphics.drawable.shapes;
 
 import android.graphics.Canvas;
+import android.graphics.Outline;
 import android.graphics.Paint;
 import android.graphics.Path;
 import android.graphics.RectF;
@@ -77,11 +78,34 @@
     public void draw(Canvas canvas, Paint paint) {
         canvas.drawPath(mPath, paint);
     }
-    
+
+    @Override
+    public boolean getOutline(Outline outline) {
+        if (mInnerRect != null) return false; // have a hole, can't produce valid outline
+
+        float radius = 0;
+        if (mOuterRadii != null) {
+            radius = mOuterRadii[0];
+            for (int i = 1; i < 8; i++) {
+                if (mOuterRadii[i] != radius) {
+                    // can't call simple constructors, use path
+                    outline.setConvexPath(mPath);
+                    return true;
+                }
+            }
+        }
+
+        final RectF rect = rect();
+        outline.setRoundRect((int) Math.ceil(rect.left), (int) Math.ceil(rect.top),
+                (int) Math.floor(rect.right), (int) Math.floor(rect.bottom),
+                radius);
+        return true;
+    }
+
     @Override
     protected void onResize(float w, float h) {
         super.onResize(w, h);
-        
+
         RectF r = rect();
         mPath.reset();
 
diff --git a/graphics/java/android/graphics/drawable/shapes/Shape.java b/graphics/java/android/graphics/drawable/shapes/Shape.java
index 4e192f95..1a20e8b 100644
--- a/graphics/java/android/graphics/drawable/shapes/Shape.java
+++ b/graphics/java/android/graphics/drawable/shapes/Shape.java
@@ -17,6 +17,7 @@
 package android.graphics.drawable.shapes;
 
 import android.graphics.Canvas;
+import android.graphics.Outline;
 import android.graphics.Paint;
 
 /**
@@ -43,7 +44,6 @@
         return mHeight;
     }
 
-
     /**
      * Draw this shape into the provided Canvas, with the provided Paint.
      * Before calling this, you must call {@link #resize(float,float)}.
@@ -52,7 +52,6 @@
      * @param paint  the Paint object that defines this shape's characteristics
      */
     public abstract void draw(Canvas canvas, Paint paint);
-    
 
     /**
      * Resizes the dimensions of this shape.
@@ -93,8 +92,20 @@
      */
     protected void onResize(float width, float height) {}
 
+    /**
+     * Compute the Outline of the shape.
+     *
+     * The default implementation does not supply an outline.
+     *
+     * @return True if a valid outline has been computed, false otherwise.
+     */
+    public boolean getOutline(Outline outline) {
+        return false;
+    }
+
     @Override
     public Shape clone() throws CloneNotSupportedException {
         return (Shape) super.clone();
     }
+
 }
diff --git a/location/java/android/location/FusedBatchOptions.java b/location/java/android/location/FusedBatchOptions.java
index 623d707..5600aeb 100644
--- a/location/java/android/location/FusedBatchOptions.java
+++ b/location/java/android/location/FusedBatchOptions.java
@@ -95,8 +95,9 @@
     }
 
     public static final class BatchFlags {
-        public static int WAKEUP_ON_FIFO_FULL = 1<<0;
-        public static int CALLBACK_ON_LOCATION_FIX = 1<<1;
+        // follow the definitions to the letter in fused_location.h
+        public static int WAKEUP_ON_FIFO_FULL = 0x0000001;
+        public static int CALLBACK_ON_LOCATION_FIX =0x0000002;
     }
 
     /*
diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java
index 72f3e1a..6424a98 100644
--- a/media/java/android/media/MediaScanner.java
+++ b/media/java/android/media/MediaScanner.java
@@ -1281,6 +1281,14 @@
         mMediaProvider = null;
     }
 
+    private void releaseResources() {
+        // release the DrmManagerClient resources
+        if (mDrmManagerClient != null) {
+            mDrmManagerClient.release();
+            mDrmManagerClient = null;
+        }
+    }
+
     private void initialize(String volumeName) {
         mMediaProvider = mContext.getContentResolver().acquireProvider("media");
 
@@ -1341,6 +1349,8 @@
             Log.e(TAG, "UnsupportedOperationException in MediaScanner.scan()", e);
         } catch (RemoteException e) {
             Log.e(TAG, "RemoteException in MediaScanner.scan()", e);
+        } finally {
+            releaseResources();
         }
     }
 
@@ -1364,6 +1374,8 @@
         } catch (RemoteException e) {
             Log.e(TAG, "RemoteException in MediaScanner.scanFile()", e);
             return null;
+        } finally {
+            releaseResources();
         }
     }
 
@@ -1511,6 +1523,7 @@
             if (fileList != null) {
                 fileList.close();
             }
+            releaseResources();
         }
     }
 
diff --git a/media/jni/android_media_ImageReader.cpp b/media/jni/android_media_ImageReader.cpp
index 716418c..7a86811 100644
--- a/media/jni/android_media_ImageReader.cpp
+++ b/media/jni/android_media_ImageReader.cpp
@@ -274,17 +274,11 @@
 
     fid = env->GetStaticFieldID(imageFormatClazz, "JPEG", "I");
     jpegFormat = env->GetStaticIntField(imageFormatClazz, fid);
-    fid = env->GetStaticFieldID(imageFormatClazz, "RAW_SENSOR", "I");
-    rawSensorFormat = env->GetStaticIntField(imageFormatClazz, fid);
 
-    // Translate the JPEG to BLOB for camera purpose, an add more if more mismatch is found.
+    // Translate the JPEG to BLOB for camera purpose.
     if (format == jpegFormat) {
         format = HAL_PIXEL_FORMAT_BLOB;
     }
-    // Same thing for RAW_SENSOR format
-    if (format == rawSensorFormat) {
-        format = HAL_PIXEL_FORMAT_RAW_SENSOR;
-    }
 
     return format;
 }
diff --git a/packages/Keyguard/res/layout/keyguard_emergency_carrier_area.xml b/packages/Keyguard/res/layout/keyguard_emergency_carrier_area.xml
index b4847f0..b2d0219 100644
--- a/packages/Keyguard/res/layout/keyguard_emergency_carrier_area.xml
+++ b/packages/Keyguard/res/layout/keyguard_emergency_carrier_area.xml
@@ -20,6 +20,7 @@
 <!-- This contains emergency call button and carrier as shared by pin/pattern/password screens -->
 <com.android.keyguard.EmergencyCarrierArea
     xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:androidprv="http://schemas.android.com/apk/res-auto"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
     android:orientation="vertical"
@@ -35,7 +36,8 @@
         android:ellipsize="marquee"
         android:textAppearance="?android:attr/textAppearanceMedium"
         android:textSize="@dimen/kg_status_line_font_size"
-        android:textColor="?android:attr/textColorSecondary" />
+        android:textColor="?android:attr/textColorSecondary"
+        androidprv:allCaps="@bool/kg_use_all_caps" />
 
     <LinearLayout
         android:layout_width="match_parent"
diff --git a/packages/Keyguard/res/values/attrs.xml b/packages/Keyguard/res/values/attrs.xml
index e045dd2..2410b6a 100644
--- a/packages/Keyguard/res/values/attrs.xml
+++ b/packages/Keyguard/res/values/attrs.xml
@@ -133,4 +133,8 @@
         <attr name="digit" format="integer" />
         <attr name="textView" format="reference" />
     </declare-styleable>
+
+    <declare-styleable name="CarrierText">
+        <attr name="allCaps" format="boolean" />
+    </declare-styleable>
 </resources>
diff --git a/packages/Keyguard/src/com/android/keyguard/CarrierText.java b/packages/Keyguard/src/com/android/keyguard/CarrierText.java
index 88558cd..05f2962 100644
--- a/packages/Keyguard/src/com/android/keyguard/CarrierText.java
+++ b/packages/Keyguard/src/com/android/keyguard/CarrierText.java
@@ -17,6 +17,7 @@
 package com.android.keyguard;
 
 import android.content.Context;
+import android.content.res.TypedArray;
 import android.text.method.SingleLineTransformationMethod;
 import android.text.TextUtils;
 import android.util.AttributeSet;
@@ -81,7 +82,14 @@
     public CarrierText(Context context, AttributeSet attrs) {
         super(context, attrs);
         mLockPatternUtils = new LockPatternUtils(mContext);
-        boolean useAllCaps = mContext.getResources().getBoolean(R.bool.kg_use_all_caps);
+        boolean useAllCaps;
+        TypedArray a = context.getTheme().obtainStyledAttributes(
+                attrs, R.styleable.CarrierText, 0, 0);
+        try {
+            useAllCaps = a.getBoolean(R.styleable.CarrierText_allCaps, false);
+        } finally {
+            a.recycle();
+        }
         setTransformationMethod(new CarrierTextTransformationMethod(mContext, useAllCaps));
     }
 
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 73c2840..ba67a82 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -215,8 +215,29 @@
         mUserHasTrust.put(userId, enabled);
     }
 
+    private boolean isTrustDisabled(int userId) {
+        final DevicePolicyManager dpm =
+                (DevicePolicyManager) mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
+        if (dpm != null) {
+                // TODO once UI is finalized
+                final boolean disabledByGlobalActions = false;
+                final boolean disabledBySettings = false;
+
+                // Don't allow trust agent if device is secured with a SIM PIN. This is here
+                // mainly because there's no other way to prompt the user to enter their SIM PIN
+                // once they get past the keyguard screen.
+                final boolean disabledBySimPin = isSimPinSecure();
+
+                final boolean disabledByDpm = (dpm.getKeyguardDisabledFeatures(null, userId)
+                        & DevicePolicyManager.KEYGUARD_DISABLE_TRUST_AGENTS) != 0;
+                return disabledByDpm || disabledByGlobalActions || disabledBySettings
+                        || disabledBySimPin;
+        }
+        return false;
+    }
+
     public boolean getUserHasTrust(int userId) {
-        return mUserHasTrust.get(userId);
+        return !isTrustDisabled(userId) && mUserHasTrust.get(userId);
     }
 
     static class DisplayClientState {
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index e1c17cb..56f5a3a 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -88,7 +88,7 @@
     <uses-permission android:name="android.permission.BLUETOOTH_STACK" />
     <uses-permission android:name="android.permission.GET_ACCOUNTS" />
     <uses-permission android:name="android.permission.RETRIEVE_WINDOW_TOKEN" />
-    <uses-permission android:name="android.permission.RENDER_STATS" />
+    <uses-permission android:name="android.permission.FRAME_STATS" />
 
     <application android:label="@string/app_label">
         <provider
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index 69fbc1b..24ccb2b 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -45,6 +45,15 @@
         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"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="2dp"
+        android:layout_marginLeft="8dp"
+        android:ellipsize="marquee"
+        android:textAppearance="?android:attr/textAppearanceMedium" />
+
     <LinearLayout
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index 2ea5add..8f3c078 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -737,8 +737,9 @@
             return false;
         }
 
-        Log.v(TAG, "publicNotification: "
-                + sbn.getNotification().publicVersion);
+        if (DEBUG) {
+            Log.v(TAG, "publicNotification: " + sbn.getNotification().publicVersion);
+        }
 
         Notification publicNotification = sbn.getNotification().publicVersion;
 
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 698aba4..0e5e2f4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -247,6 +247,7 @@
     private int mCarrierLabelHeight;
     private TextView mEmergencyCallLabel;
     private int mNotificationHeaderHeight;
+    private View mKeyguardCarrierLabel;
 
     private boolean mShowCarrierInPanel = false;
 
@@ -375,6 +376,7 @@
     private InterceptedNotifications mIntercepted;
     private VelocityTracker mSettingsTracker;
     private float mSettingsDownY;
+    private boolean mSettingsStarted;
     private boolean mSettingsCancelled;
     private boolean mSettingsClosing;
     private int mNotificationPadding;
@@ -615,6 +617,7 @@
                 (NotificationOverflowContainer) LayoutInflater.from(mContext).inflate(
                         R.layout.status_bar_notification_keyguard_overflow, mStackScroller, false);
         mKeyguardIconOverflowContainer.setOnActivatedListener(this);
+        mKeyguardCarrierLabel = mStatusBarWindow.findViewById(R.id.keyguard_carrier_text);
         mStackScroller.addView(mKeyguardIconOverflowContainer);
 
         mExpandedContents = mStackScroller;
@@ -766,16 +769,16 @@
         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;
-            mFlipSettingsView.setVisibility(View.VISIBLE);
-            mStackScroller.setVisibility(View.VISIBLE);
-            positionSettings(0);
-            if (!mSettingsClosing) {
+            if (mSettingsClosing) {
+                mStackScroller.setVisibility(View.VISIBLE);
+            } else {
                 mFlipSettingsView.setTranslationY(-mNotificationPanel.getMeasuredHeight());
             }
             dispatchSettingsEvent(event);
@@ -784,8 +787,7 @@
             final float dy = event.getY() - mSettingsDownY;
             final FlipperButton flipper = mOnKeyguard ? mKeyguardFlipper : mHeaderFlipper;
             final boolean inButton = flipper.inHolderBounds(event);
-            final int slop = ViewConfiguration.get(mContext).getScaledTouchSlop();
-            final boolean qsTap = mSettingsClosing &&  Math.abs(dy) < slop;
+            final boolean qsTap = mSettingsClosing && Math.abs(dy) < slop;
             if (!qsTap && !inButton) {
                 mSettingsTracker.computeCurrentVelocity(1000);
                 final float vy = mSettingsTracker.getYVelocity();
@@ -801,10 +803,9 @@
             dispatchSettingsEvent(event);
         } else if (mSettingsTracker != null && event.getAction() == MotionEvent.ACTION_MOVE) {
             final float dy = event.getY() - mSettingsDownY;
-            positionSettings(dy);
             if (mSettingsClosing) {
-                final boolean qsTap =
-                        Math.abs(dy) < ViewConfiguration.get(mContext).getScaledTouchSlop();
+                positionSettings(dy);
+                final boolean qsTap = Math.abs(dy) < slop;
                 if (!mSettingsCancelled && !qsTap) {
                     MotionEvent cancelEvent = MotionEvent.obtainNoHistory(event);
                     cancelEvent.setAction(MotionEvent.ACTION_CANCEL);
@@ -812,6 +813,14 @@
                     mSettingsCancelled = true;
                 }
             } else {
+                if (!mSettingsStarted && dy > slop) {
+                    mSettingsStarted = true;
+                    mFlipSettingsView.setVisibility(View.VISIBLE);
+                    mStackScroller.setVisibility(View.VISIBLE);
+                }
+                if (mSettingsStarted) {
+                    positionSettings(dy);
+                }
                 dispatchSettingsEvent(event);
             }
         }
@@ -1337,7 +1346,8 @@
             !(emergencyCallsShownElsewhere && mNetworkController.isEmergencyOnly())
             && mStackScroller.getHeight() < (mNotificationPanel.getHeight()
                     - mCarrierLabelHeight - mNotificationHeaderHeight)
-            && mStackScroller.getVisibility() == View.VISIBLE;
+            && mStackScroller.getVisibility() == View.VISIBLE
+            && !mOnKeyguard;
 
         if (force || mCarrierLabelVisible != makeVisible) {
             mCarrierLabelVisible = makeVisible;
@@ -2949,6 +2959,7 @@
             mKeyguardBottomArea.setVisibility(View.VISIBLE);
             mKeyguardIndicationTextView.setVisibility(View.VISIBLE);
             mKeyguardIndicationTextView.switchIndication(mKeyguardHotwordPhrase);
+            mKeyguardCarrierLabel.setVisibility(View.VISIBLE);
             mNotificationPanelHeader.setVisibility(View.GONE);
 
             mKeyguardFlipper.setVisibility(View.VISIBLE);
@@ -2957,6 +2968,7 @@
             mKeyguardStatusView.setVisibility(View.GONE);
             mKeyguardBottomArea.setVisibility(View.GONE);
             mKeyguardIndicationTextView.setVisibility(View.GONE);
+            mKeyguardCarrierLabel.setVisibility(View.GONE);
             mNotificationPanelHeader.setVisibility(View.VISIBLE);
 
             mKeyguardFlipper.setVisibility(View.GONE);
@@ -2967,6 +2979,7 @@
         updateRowStates();
         checkBarModes();
         updateNotificationIcons();
+        updateCarrierLabelVisibility(false);
     }
 
     public void userActivity() {
@@ -2995,14 +3008,22 @@
     }
 
     private void instantExpandNotificationsPanel() {
+
+        // Make our window larger.
+        mStatusBarWindowManager.setStatusBarExpanded(true);
+
+        // Wait for window manager to pickup the change, so we know the maximum height of the panel
+        // then.
         mNotificationPanel.getViewTreeObserver().addOnGlobalLayoutListener(
                 new ViewTreeObserver.OnGlobalLayoutListener() {
-                    @Override
-                    public void onGlobalLayout() {
-                        mNotificationPanel.getViewTreeObserver().removeOnGlobalLayoutListener(this);
-                        mNotificationPanel.setExpandedFraction(1);
-                    }
-                });
+            @Override
+            public void onGlobalLayout() {
+                if (mStatusBarWindow.getHeight() != getStatusBarHeight()) {
+                    mNotificationPanel.getViewTreeObserver().removeOnGlobalLayoutListener(this);
+                    mNotificationPanel.setExpandedFraction(1);
+                }
+            }
+        });
     }
 
     private void instantCollapseNotificationPanel() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
index 8957a77..194774d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -168,6 +168,9 @@
         if (IccCardConstants.INTENT_VALUE_ICC_ABSENT.equals(stateExtra)) {
             mSimState = IccCardConstants.State.ABSENT;
         }
+        else if (IccCardConstants.INTENT_VALUE_ICC_CARD_IO_ERROR.equals(stateExtra)) {
+            mSimState = IccCardConstants.State.CARD_IO_ERROR;
+        }
         else if (IccCardConstants.INTENT_VALUE_ICC_READY.equals(stateExtra)) {
             mSimState = IccCardConstants.State.READY;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ZenModeView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ZenModeView.java
index c1662ba..20011ff 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ZenModeView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ZenModeView.java
@@ -212,6 +212,13 @@
         }
     }
 
+    @Override
+    protected void onDetachedFromWindow() {
+        if (mAdapter != null) {
+            mAdapter.dispose();
+        }
+    }
+
     public void setAutoActivate(boolean value) {
         mAutoActivate = value;
     }
@@ -396,6 +403,7 @@
         void setMode(boolean mode);
         void select(ExitCondition ec);
         void init();
+        void dispose();
         void setCallbacks(Callbacks callbacks);
         ExitCondition getExitCondition(int d);
         int getExitConditionCount();
@@ -406,6 +414,7 @@
             public String line1;
             public String line2;
             public String action;
+            public Object tag;
         }
 
         public interface Callbacks {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ZenModeViewAdapter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ZenModeViewAdapter.java
index c97ba8d..a5e016a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ZenModeViewAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ZenModeViewAdapter.java
@@ -16,14 +16,22 @@
 
 package com.android.systemui.statusbar.phone;
 
+import android.app.INotificationManager;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.database.ContentObserver;
+import android.net.Uri;
 import android.os.AsyncTask;
 import android.os.Handler;
+import android.os.RemoteException;
+import android.os.ServiceManager;
 import android.provider.Settings;
+import android.service.notification.Condition;
+import android.service.notification.IConditionListener;
+import android.util.ArrayMap;
 import android.util.Log;
 
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 
@@ -34,8 +42,10 @@
     private final ContentResolver mResolver;
     private final Handler mHandler = new Handler();
     private final SettingsObserver mObserver;
-    private final List<ExitCondition> mExits = Arrays.asList(
-            newExit("Until you turn this off", "Until", "You turn this off"));
+    private final List<ExitCondition> mExits = new ArrayList<ExitCondition>(Arrays.asList(
+            newExit("Until you turn this off", "Until", "You turn this off", null)));
+    private final INotificationManager mNoMan;
+    private final ArrayMap<Uri, Condition> mConditions = new ArrayMap<Uri, Condition>();
 
     private Callbacks mCallbacks;
     private int mExitIndex;
@@ -45,6 +55,13 @@
         mContext = context;
         mResolver = mContext.getContentResolver();
         mObserver = new SettingsObserver(mHandler);
+        mNoMan = INotificationManager.Stub.asInterface(
+                ServiceManager.getService(Context.NOTIFICATION_SERVICE));
+        try {
+            mNoMan.requestZenModeConditions(mListener, true /*requested*/);
+        } catch (RemoteException e) {
+            // noop
+        }
         mObserver.init();
         init();
     }
@@ -77,6 +94,15 @@
         }
     }
 
+    @Override
+    public void dispose() {
+        try {
+            mNoMan.requestZenModeConditions(mListener, false /*requested*/);
+        } catch (RemoteException e) {
+            // noop
+        }
+    }
+
     private void dispatchChanged() {
         mHandler.removeCallbacks(mChanged);
         mHandler.post(mChanged);
@@ -117,13 +143,20 @@
         }
         mExitIndex = i;
         dispatchChanged();
+        final Uri conditionUri = (Uri) ec.tag;
+        try {
+            mNoMan.setZenModeCondition(conditionUri);
+        } catch (RemoteException e) {
+            // noop
+        }
     }
 
-    private static ExitCondition newExit(String summary, String line1, String line2) {
+    private static ExitCondition newExit(String summary, String line1, String line2, Object tag) {
         final ExitCondition rt = new ExitCondition();
         rt.summary = summary;
         rt.line1 = line1;
         rt.line2 = line2;
+        rt.tag = tag;
         return rt;
     }
 
@@ -168,4 +201,21 @@
             return v != Settings.Global.ZEN_MODE_OFF;
         }
     }
+
+    private final IConditionListener mListener = new IConditionListener.Stub() {
+        @Override
+        public void onConditionsReceived(Condition[] conditions) {
+            if (conditions == null || conditions.length == 0) return;
+            for (Condition c : conditions) {
+                mConditions.put(c.id, c);
+            }
+            for (int i = mExits.size() - 1; i > 0; i--) {
+                mExits.remove(i);
+            }
+            for (Condition c : mConditions.values()) {
+                mExits.add(newExit(c.caption, "", "", c.id));
+            }
+            dispatchChanged();
+        }
+    };
 }
diff --git a/services/Android.mk b/services/Android.mk
index 165f456..5fcef64 100644
--- a/services/Android.mk
+++ b/services/Android.mk
@@ -25,7 +25,8 @@
     backup \
     devicepolicy \
     print \
-    usb
+    usb \
+    voiceinteraction
 
 # The convention is to name each service module 'services.$(module_name)'
 LOCAL_STATIC_JAVA_LIBRARIES := $(addprefix services.,$(services))
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index ed007e9..b6a41bf 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -28,19 +28,23 @@
 import static org.xmlpull.v1.XmlPullParser.START_TAG;
 import static com.android.server.am.ActivityStackSupervisor.HOME_STACK_ID;
 
+import android.Manifest;
 import android.app.AppOpsManager;
 import android.app.IActivityContainer;
 import android.app.IActivityContainerCallback;
 import android.appwidget.AppWidgetManager;
 import android.graphics.Rect;
 import android.os.BatteryStats;
+import android.service.voice.IVoiceInteractionSession;
 import android.util.ArrayMap;
 
 import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.app.IAppOpsService;
+import com.android.internal.app.IVoiceInteractor;
 import com.android.internal.app.ProcessMap;
 import com.android.internal.app.ProcessStats;
+import com.android.internal.content.PackageMonitor;
 import com.android.internal.os.BackgroundThread;
 import com.android.internal.os.BatteryStatsImpl;
 import com.android.internal.os.ProcessCpuTracker;
@@ -56,6 +60,7 @@
 import com.android.server.LocalServices;
 import com.android.server.ServiceThread;
 import com.android.server.SystemService;
+import com.android.server.SystemServiceManager;
 import com.android.server.Watchdog;
 import com.android.server.am.ActivityStack.ActivityState;
 import com.android.server.firewall.IntentFirewall;
@@ -333,6 +338,9 @@
     // How many bytes to write into the dropbox log before truncating
     static final int DROPBOX_MAX_SIZE = 256 * 1024;
 
+    /** All system services */
+    SystemServiceManager mSystemServiceManager;
+
     /** Run all ActivityStacks through this */
     ActivityStackSupervisor mStackSupervisor;
 
@@ -399,7 +407,7 @@
     /**
      * List of intents that were used to start the most recent tasks.
      */
-    private final ArrayList<TaskRecord> mRecentTasks = new ArrayList<TaskRecord>();
+    final ArrayList<TaskRecord> mRecentTasks = new ArrayList<TaskRecord>();
 
     public class PendingAssistExtras extends Binder implements Runnable {
         public final ActivityRecord activity;
@@ -879,17 +887,23 @@
      * Set while we are wanting to sleep, to prevent any
      * activities from being started/resumed.
      */
-    boolean mSleeping = false;
+    private boolean mSleeping = false;
+
+    /**
+     * Set while we are running a voice interaction.  This overrides
+     * sleeping while it is active.
+     */
+    private boolean mRunningVoice = false;
 
     /**
      * State of external calls telling us if the device is asleep.
      */
-    boolean mWentToSleep = false;
+    private boolean mWentToSleep = false;
 
     /**
      * State of external call telling us if the lock screen is shown.
      */
-    boolean mLockScreenShown = false;
+    private boolean mLockScreenShown = false;
 
     /**
      * Set if we are shutting down the system, similar to sleeping.
@@ -1117,6 +1131,8 @@
     static final int REQUEST_ALL_PSS_MSG = 39;
     static final int START_PROFILES_MSG = 40;
     static final int UPDATE_TIME = 41;
+    static final int SYSTEM_USER_START_MSG = 42;
+    static final int SYSTEM_USER_CURRENT_MSG = 43;
 
     static final int FIRST_ACTIVITY_STACK_MSG = 100;
     static final int FIRST_BROADCAST_QUEUE_MSG = 200;
@@ -1696,15 +1712,15 @@
                 break;
             }
             case REPORT_USER_SWITCH_MSG: {
-                dispatchUserSwitch((UserStartedState)msg.obj, msg.arg1, msg.arg2);
+                dispatchUserSwitch((UserStartedState) msg.obj, msg.arg1, msg.arg2);
                 break;
             }
             case CONTINUE_USER_SWITCH_MSG: {
-                continueUserSwitch((UserStartedState)msg.obj, msg.arg1, msg.arg2);
+                continueUserSwitch((UserStartedState) msg.obj, msg.arg1, msg.arg2);
                 break;
             }
             case USER_SWITCH_TIMEOUT_MSG: {
-                timeoutUserSwitch((UserStartedState)msg.obj, msg.arg1, msg.arg2);
+                timeoutUserSwitch((UserStartedState) msg.obj, msg.arg1, msg.arg2);
                 break;
             }
             case IMMERSIVE_MODE_LOCK_MSG: {
@@ -1751,6 +1767,14 @@
                 }
                 break;
             }
+            case SYSTEM_USER_START_MSG: {
+                mSystemServiceManager.startUser(msg.arg1);
+                break;
+            }
+            case SYSTEM_USER_CURRENT_MSG: {
+                mSystemServiceManager.switchUser(msg.arg1);
+                break;
+            }
             }
         }
     };
@@ -1813,6 +1837,78 @@
         }
     };
 
+    /**
+     * Monitor for package changes and update our internal state.
+     */
+    private final PackageMonitor mPackageMonitor = new PackageMonitor() {
+        @Override
+        public void onPackageRemoved(String packageName, int uid) {
+            // Remove all tasks with activities in the specified package from the list of recent tasks
+            synchronized (ActivityManagerService.this) {
+                for (int i = mRecentTasks.size() - 1; i >= 0; i--) {
+                    TaskRecord tr = mRecentTasks.get(i);
+                    ComponentName cn = tr.intent.getComponent();
+                    if (cn != null && cn.getPackageName().equals(packageName)) {
+                        // If the package name matches, remove the task and kill the process
+                        removeTaskByIdLocked(tr.taskId, ActivityManager.REMOVE_TASK_KILL_PROCESS);
+                    }
+                }
+            }
+        }
+
+        @Override
+        public boolean onPackageChanged(String packageName, int uid, String[] components) {
+            final PackageManager pm = mContext.getPackageManager();
+            final ArrayList<TaskRecord> recentTasks = new ArrayList<TaskRecord>();
+            final ArrayList<TaskRecord> tasksToRemove = new ArrayList<TaskRecord>();
+            // Copy the list of recent tasks so that we don't hold onto the lock on
+            // ActivityManagerService for long periods while checking if components exist.
+            synchronized (ActivityManagerService.this) {
+                recentTasks.addAll(mRecentTasks);
+            }
+            // Check the recent tasks and filter out all tasks with components that no longer exist.
+            Intent tmpI = new Intent();
+            for (int i = recentTasks.size() - 1; i >= 0; i--) {
+                TaskRecord tr = recentTasks.get(i);
+                ComponentName cn = tr.intent.getComponent();
+                if (cn != null && cn.getPackageName().equals(packageName)) {
+                    try {
+                        // Add the task to the list to remove if the component no longer exists
+                        tmpI.setComponent(cn);
+                        if (pm.queryIntentActivities(tmpI, PackageManager.MATCH_DEFAULT_ONLY).isEmpty()) {
+                            tasksToRemove.add(tr);
+                        }
+                    } catch (Exception e) {}
+                }
+            }
+            // Prune all the tasks with removed components from the list of recent tasks
+            synchronized (ActivityManagerService.this) {
+                for (int i = tasksToRemove.size() - 1; i >= 0; i--) {
+                    TaskRecord tr = tasksToRemove.get(i);
+                    // Remove the task but don't kill the process
+                    removeTaskByIdLocked(tr.taskId, 0);
+                }
+            }
+            return true;
+        }
+
+        @Override
+        public boolean onHandleForceStop(Intent intent, String[] packages, int uid, boolean doit) {
+            // Force stop the specified packages
+            if (packages != null) {
+                for (String pkg : packages) {
+                    synchronized (ActivityManagerService.this) {
+                        if (forceStopPackageLocked(pkg, -1, false, false, false, false, false, 0,
+                                "finished booting")) {
+                            return true;
+                        }
+                    }
+                }
+            }
+            return false;
+        }
+    };
+
     public void setSystemProcess() {
         try {
             ServiceManager.addService(Context.ACTIVITY_SERVICE, this, true);
@@ -2059,6 +2155,10 @@
         Watchdog.getInstance().addThread(mHandler);
     }
 
+    public void setSystemServiceManager(SystemServiceManager mgr) {
+        mSystemServiceManager = mgr;
+    }
+
     private void start() {
         mProcessCpuThread.start();
 
@@ -2254,6 +2354,11 @@
         if (mFocusedActivity != r) {
             if (DEBUG_FOCUS) Slog.d(TAG, "setFocusedActivityLocked: r=" + r);
             mFocusedActivity = r;
+            if (r.task != null && r.task.voiceInteractor != null) {
+                startRunningVoiceLocked();
+            } else {
+                finishRunningVoiceLocked();
+            }
             mStackSupervisor.setFocusedStack(r);
             if (r != null) {
                 mWindowManager.setFocusedApp(r.appToken, true);
@@ -2262,6 +2367,12 @@
         }
     }
 
+    final void clearFocusedActivity(ActivityRecord r) {
+        if (mFocusedActivity == r) {
+            mFocusedActivity = null;
+        }
+    }
+
     @Override
     public void setFocusedStack(int stackId) {
         if (DEBUG_FOCUS) Slog.d(TAG, "setFocusedStack: stackId=" + stackId);
@@ -2990,7 +3101,7 @@
                     intent.setComponent(new ComponentName(
                             ri.activityInfo.packageName, ri.activityInfo.name));
                     mStackSupervisor.startActivityLocked(null, intent, null, ri.activityInfo,
-                            null, null, 0, 0, 0, null, 0, null, false, null, null);
+                            null, null, null, null, 0, 0, 0, null, 0, null, false, null, null);
                 }
             }
         }
@@ -3121,7 +3232,7 @@
         }
         for (int i=0; i<N; i++) {
             PendingActivityLaunch pal = mPendingActivityLaunches.get(i);
-            mStackSupervisor.startActivityUncheckedLocked(pal.r, pal.sourceRecord, pal.startFlags,
+            mStackSupervisor.startActivityUncheckedLocked(pal.r, pal.sourceRecord, null, null, pal.startFlags,
                     doResume && i == (N-1), null);
         }
         mPendingActivityLaunches.clear();
@@ -3147,7 +3258,7 @@
                 false, true, "startActivity", null);
         // TODO: Switch to user app stacks here.
         return mStackSupervisor.startActivityMayWait(caller, -1, callingPackage, intent, resolvedType,
-                resultTo, resultWho, requestCode, startFlags, profileFile, profileFd,
+                null, null, resultTo, resultWho, requestCode, startFlags, profileFile, profileFd,
                 null, null, options, userId, null);
     }
 
@@ -3162,7 +3273,7 @@
         WaitResult res = new WaitResult();
         // TODO: Switch to user app stacks here.
         mStackSupervisor.startActivityMayWait(caller, -1, callingPackage, intent, resolvedType,
-                resultTo, resultWho, requestCode, startFlags, profileFile, profileFd,
+                null, null, resultTo, resultWho, requestCode, startFlags, profileFile, profileFd,
                 res, null, options, UserHandle.getCallingUserId(), null);
         return res;
     }
@@ -3177,7 +3288,7 @@
                 false, true, "startActivityWithConfig", null);
         // TODO: Switch to user app stacks here.
         int ret = mStackSupervisor.startActivityMayWait(caller, -1, callingPackage, intent,
-                resolvedType, resultTo, resultWho, requestCode, startFlags,
+                resolvedType, null, null, resultTo, resultWho, requestCode, startFlags,
                 null, null, null, config, options, userId, null);
         return ret;
     }
@@ -3215,6 +3326,31 @@
     }
 
     @Override
+    public int startVoiceActivity(String callingPackage, int callingPid, int callingUid,
+            Intent intent, String resolvedType, IVoiceInteractionSession session,
+            IVoiceInteractor interactor, int startFlags, String profileFile,
+            ParcelFileDescriptor profileFd, Bundle options, int userId) {
+        if (checkCallingPermission(Manifest.permission.BIND_VOICE_INTERACTION)
+                != PackageManager.PERMISSION_GRANTED) {
+            String msg = "Permission Denial: startVoiceActivity() from pid="
+                    + Binder.getCallingPid()
+                    + ", uid=" + Binder.getCallingUid()
+                    + " requires " + android.Manifest.permission.BIND_VOICE_INTERACTION;
+            Slog.w(TAG, msg);
+            throw new SecurityException(msg);
+        }
+        if (session == null || interactor == null) {
+            throw new NullPointerException("null session or interactor");
+        }
+        userId = handleIncomingUser(callingPid, callingUid, userId,
+                false, true, "startVoiceActivity", null);
+        // TODO: Switch to user app stacks here.
+        return mStackSupervisor.startActivityMayWait(null, callingUid, callingPackage, intent,
+                resolvedType, session, interactor, null, null, 0, startFlags,
+                profileFile, profileFd, null, null, options, userId, null);
+    }
+
+    @Override
     public boolean startNextMatchingActivity(IBinder callingActivity,
             Intent intent, Bundle options) {
         // Refuse possible leaked file descriptors
@@ -3307,7 +3443,7 @@
 
             final long origId = Binder.clearCallingIdentity();
             int res = mStackSupervisor.startActivityLocked(r.app.thread, intent,
-                    r.resolvedType, aInfo, resultTo != null ? resultTo.appToken : null,
+                    r.resolvedType, aInfo, null, null, resultTo != null ? resultTo.appToken : null,
                     resultWho, requestCode, -1, r.launchedFromUid, r.launchedFromPackage, 0,
                     options, false, null, null);
             Binder.restoreCallingIdentity(origId);
@@ -3330,7 +3466,7 @@
 
         // TODO: Switch to user app stacks here.
         int ret = mStackSupervisor.startActivityMayWait(null, uid, callingPackage, intent, resolvedType,
-                resultTo, resultWho, requestCode, startFlags,
+                null, null, resultTo, resultWho, requestCode, startFlags,
                 null, null, null, null, options, userId, container);
         return ret;
     }
@@ -3366,6 +3502,10 @@
         if (N > 0 && mRecentTasks.get(0) == task) {
             return;
         }
+        // Another quick case: never add voice sessions.
+        if (task.voiceSession != null) {
+            return;
+        }
         // Remove any existing entries that are the same kind of task.
         final Intent intent = task.intent;
         final boolean document = intent != null && intent.isDocument();
@@ -5200,26 +5340,8 @@
     }
 
     final void finishBooting() {
-        IntentFilter pkgFilter = new IntentFilter();
-        pkgFilter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
-        pkgFilter.addDataScheme("package");
-        mContext.registerReceiver(new BroadcastReceiver() {
-            @Override
-            public void onReceive(Context context, Intent intent) {
-                String[] pkgs = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES);
-                if (pkgs != null) {
-                    for (String pkg : pkgs) {
-                        synchronized (ActivityManagerService.this) {
-                            if (forceStopPackageLocked(pkg, -1, false, false, false, false, false, 0,
-                                    "finished booting")) {
-                                setResultCode(Activity.RESULT_OK);
-                                return;
-                            }
-                        }
-                    }
-                }
-            }
-        }, pkgFilter);
+        // Register receivers to handle package update events
+        mPackageMonitor.register(mContext, Looper.getMainLooper(), false);
 
         synchronized (this) {
             // Ensure that any processes we had put on hold are now started
@@ -8509,11 +8631,27 @@
         return mSleeping || mShuttingDown;
     }
 
+    public boolean isSleeping() {
+        return mSleeping;
+    }
+
     void goingToSleep() {
         synchronized(this) {
             mWentToSleep = true;
             updateEventDispatchingLocked();
+            goToSleepIfNeededLocked();
+        }
+    }
 
+    void finishRunningVoiceLocked() {
+        if (mRunningVoice) {
+            mRunningVoice = false;
+            goToSleepIfNeededLocked();
+        }
+    }
+
+    void goToSleepIfNeededLocked() {
+        if (mWentToSleep && !mRunningVoice) {
             if (!mSleeping) {
                 mSleeping = true;
                 mStackSupervisor.goingToSleepLocked();
@@ -8576,7 +8714,7 @@
     }
 
     private void comeOutOfSleepIfNeededLocked() {
-        if (!mWentToSleep && !mLockScreenShown) {
+        if ((!mWentToSleep && !mLockScreenShown) || mRunningVoice) {
             if (mSleeping) {
                 mSleeping = false;
                 mStackSupervisor.comeOutOfSleepIfNeededLocked();
@@ -8592,6 +8730,13 @@
         }
     }
 
+    void startRunningVoiceLocked() {
+        if (!mRunningVoice) {
+            mRunningVoice = true;
+            comeOutOfSleepIfNeededLocked();
+        }
+    }
+
     private void updateEventDispatchingLocked() {
         mWindowManager.setEventDispatching(mBooted && !mWentToSleep && !mShuttingDown);
     }
@@ -9298,7 +9443,7 @@
                     proc.notCachedSinceIdle = true;
                     proc.initialIdlePss = 0;
                     proc.nextPssTime = ProcessList.computeNextPssTime(proc.curProcState, true,
-                            mSleeping, now);
+                            isSleeping(), now);
                 }
             }
 
@@ -9597,6 +9742,8 @@
 
         if (goingCallback != null) goingCallback.run();
         
+        mSystemServiceManager.startUser(mCurrentUserId);
+
         synchronized (this) {
             if (mFactoryTest != FactoryTest.FACTORY_TEST_LOW_LEVEL) {
                 try {
@@ -9653,6 +9800,8 @@
                         }, 0, null, null,
                         android.Manifest.permission.INTERACT_ACROSS_USERS, AppOpsManager.OP_NONE,
                         true, false, MY_PID, Process.SYSTEM_UID, UserHandle.USER_ALL);
+            } catch (Throwable t) {
+                Slog.wtf(TAG, "Failed sending first user broadcasts", t);
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
@@ -11152,8 +11301,8 @@
                 pw.println("  mSleeping=" + mSleeping + " mWentToSleep=" + mWentToSleep
                         + " mLockScreenShown " + mLockScreenShown);
             }
-            if (mShuttingDown) {
-                pw.println("  mShuttingDown=" + mShuttingDown);
+            if (mShuttingDown || mRunningVoice) {
+                pw.print("  mShuttingDown=" + mShuttingDown + " mRunningVoice=" + mRunningVoice);
             }
         }
         if (mDebugApp != null || mOrigDebugApp != null || mDebugTransient
@@ -15273,7 +15422,7 @@
             if (memLowered || now > (app.lastStateTime+ProcessList.PSS_ALL_INTERVAL)) {
                 app.pssProcState = app.setProcState;
                 app.nextPssTime = ProcessList.computeNextPssTime(app.curProcState, true,
-                        mSleeping, now);
+                        isSleeping(), now);
                 mPendingPssProcesses.add(app);
             }
         }
@@ -15310,7 +15459,7 @@
             }
         }
         return !processingBroadcasts
-                && (mSleeping || mStackSupervisor.allResumedActivitiesIdle());
+                && (isSleeping() || mStackSupervisor.allResumedActivitiesIdle());
     }
     
     /**
@@ -15585,7 +15734,7 @@
                 app.setProcState)) {
             app.lastStateTime = now;
             app.nextPssTime = ProcessList.computeNextPssTime(app.curProcState, true,
-                    mSleeping, now);
+                    isSleeping(), now);
             if (DEBUG_PSS) Slog.d(TAG, "Process state change from "
                     + ProcessList.makeProcStateString(app.setProcState) + " to "
                     + ProcessList.makeProcStateString(app.curProcState) + " next pss in "
@@ -15595,7 +15744,7 @@
                     && now > (app.lastStateTime+ProcessList.PSS_MIN_TIME_FROM_STATE_CHANGE))) {
                 requestPssLocked(app, app.setProcState);
                 app.nextPssTime = ProcessList.computeNextPssTime(app.curProcState, false,
-                        mSleeping, now);
+                        isSleeping(), now);
             } else if (false && DEBUG_PSS) {
                 Slog.d(TAG, "Not requesting PSS of " + app + ": next=" + (app.nextPssTime-now));
             }
@@ -15932,7 +16081,7 @@
         }
         mLastMemoryLevel = memFactor;
         mLastNumProcesses = mLruProcesses.size();
-        boolean allChanged = mProcessStats.setMemFactorLocked(memFactor, !mSleeping, now);
+        boolean allChanged = mProcessStats.setMemFactorLocked(memFactor, !isSleeping(), now);
         final int trackerMemFactor = mProcessStats.getMemFactorLocked();
         if (memFactor != ProcessStats.ADJ_MEM_FACTOR_NORMAL) {
             if (mLowRamStartTime == 0) {
@@ -16475,7 +16624,15 @@
                     needStart = true;
                 }
 
+                if (uss.mState == UserStartedState.STATE_BOOTING) {
+                    // Booting up a new user, need to tell system services about it.
+                    // Note that this is on the same handler as scheduling of broadcasts,
+                    // which is important because it needs to go first.
+                    mHandler.sendMessage(mHandler.obtainMessage(SYSTEM_USER_START_MSG, userId));
+                }
+
                 if (foreground) {
+                    mHandler.sendMessage(mHandler.obtainMessage(SYSTEM_USER_CURRENT_MSG, userId));
                     mHandler.removeMessages(REPORT_USER_SWITCH_MSG);
                     mHandler.removeMessages(USER_SWITCH_TIMEOUT_MSG);
                     mHandler.sendMessage(mHandler.obtainMessage(REPORT_USER_SWITCH_MSG,
@@ -16830,6 +16987,7 @@
                             }
                             uss.mState = UserStartedState.STATE_SHUTDOWN;
                         }
+                        mSystemServiceManager.stopUser(userId);
                         broadcastIntentLocked(null, null, shutdownIntent,
                                 null, shutdownReceiver, 0, null, null, null, AppOpsManager.OP_NONE,
                                 true, false, MY_PID, Process.SYSTEM_UID, userId);
@@ -16879,7 +17037,12 @@
             }
         }
 
-        mStackSupervisor.removeUserLocked(userId);
+        if (stopped) {
+            mSystemServiceManager.cleanupUser(userId);
+            synchronized (this) {
+                mStackSupervisor.removeUserLocked(userId);
+            }
+        }
     }
 
     @Override
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 3e59def..7a44473 100755
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -633,7 +633,7 @@
         // case we will deliver it if this is the current top activity on its
         // stack.
         boolean unsent = true;
-        if ((state == ActivityState.RESUMED || (service.mSleeping
+        if ((state == ActivityState.RESUMED || (service.isSleeping()
                         && task.stack.topRunningActivityLocked(null) == this))
                 && app != null && app.thread != null) {
             try {
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index a2a9336..bea926f 100755
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -38,6 +38,8 @@
 
 import static com.android.server.am.ActivityStackSupervisor.ActivityContainer.CONTAINER_STATE_HAS_SURFACE;
 
+import android.service.voice.IVoiceInteractionSession;
+import com.android.internal.app.IVoiceInteractor;
 import com.android.internal.os.BatteryStatsImpl;
 import com.android.server.Watchdog;
 import com.android.server.am.ActivityManagerService.ItemMatcher;
@@ -501,6 +503,11 @@
         if (DEBUG_TASKS) Slog.d(TAG, "Looking for task of " + target + " in " + this);
         for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
             final TaskRecord task = mTaskHistory.get(taskNdx);
+            if (task.voiceSession != null) {
+                // We never match voice sessions; those always run independently.
+                if (DEBUG_TASKS) Slog.d(TAG, "Skipping " + task + ": voice session");
+                continue;
+            }
             if (task.userId != userId) {
                 // Looking for a different task.
                 if (DEBUG_TASKS) Slog.d(TAG, "Skipping " + task + ": different user");
@@ -1503,7 +1510,7 @@
         // If the most recent activity was noHistory but was only stopped rather
         // than stopped+finished because the device went to sleep, we need to make
         // sure to finish it as we're making a new activity topmost.
-        if (mService.mSleeping && mLastNoHistoryActivity != null &&
+        if (mService.isSleeping() && mLastNoHistoryActivity != null &&
                 !mLastNoHistoryActivity.finishing) {
             if (DEBUG_STATES) Slog.d(TAG, "no-history finish of " + mLastNoHistoryActivity +
                     " on new resume");
@@ -2034,7 +2041,7 @@
                             + " out to bottom task " + bottom.task);
                 } else {
                     targetTask = createTaskRecord(mStackSupervisor.getNextTaskId(), target.info,
-                            null, false);
+                            null, null, null, false);
                     newThumbHolder = targetTask;
                     targetTask.affinityIntent = target.intent;
                     if (DEBUG_TASKS) Slog.v(TAG, "Start pushing activity " + target
@@ -2331,7 +2338,7 @@
         if ((r.intent.getFlags()&Intent.FLAG_ACTIVITY_NO_HISTORY) != 0
                 || (r.info.flags&ActivityInfo.FLAG_NO_HISTORY) != 0) {
             if (!r.finishing) {
-                if (!mService.mSleeping) {
+                if (!mService.isSleeping()) {
                     if (DEBUG_STATES) {
                         Slog.d(TAG, "no-history finish of " + r);
                     }
@@ -2710,7 +2717,7 @@
                     ActivityInfo aInfo = AppGlobals.getPackageManager().getActivityInfo(
                             destIntent.getComponent(), 0, srec.userId);
                     int res = mStackSupervisor.startActivityLocked(srec.app.thread, destIntent,
-                            null, aInfo, parent.appToken, null,
+                            null, aInfo, null, null, parent.appToken, null,
                             0, -1, parent.launchedFromUid, parent.launchedFromPackage,
                             0, null, true, null, null);
                     foundParentInTask = res == ActivityManager.START_SUCCESS;
@@ -2739,9 +2746,7 @@
         if (mPausingActivity == r) {
             mPausingActivity = null;
         }
-        if (mService.mFocusedActivity == r) {
-            mService.mFocusedActivity = null;
-        }
+        mService.clearFocusedActivity(r);
 
         r.configDestroy = false;
         r.frozenBeforeDestroy = false;
@@ -3723,6 +3728,11 @@
             mTaskHistory.get(taskNdx + 1).mOnTopOfHome = true;
         }
         mTaskHistory.remove(task);
+        if (task.voiceInteractor != null) {
+            // This task was a voice interaction, so it should not remain on the
+            // recent tasks list.
+            mService.mRecentTasks.remove(task);
+        }
 
         if (mTaskHistory.isEmpty()) {
             if (DEBUG_STACK) Slog.i(TAG, "removeTask: moving to back stack=" + this);
@@ -3736,8 +3746,10 @@
         }
     }
 
-    TaskRecord createTaskRecord(int taskId, ActivityInfo info, Intent intent, boolean toTop) {
-        TaskRecord task = new TaskRecord(taskId, info, intent);
+    TaskRecord createTaskRecord(int taskId, ActivityInfo info, Intent intent,
+            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
+            boolean toTop) {
+        TaskRecord task = new TaskRecord(taskId, info, intent, voiceSession, voiceInteractor);
         addTask(task, toTop);
         return task;
     }
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index e044d3f..3770a07 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -17,7 +17,6 @@
 package com.android.server.am;
 
 import static android.Manifest.permission.START_ANY_ACTIVITY;
-import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
 import static android.content.Intent.FLAG_ACTIVITY_TASK_ON_HOME;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
@@ -77,6 +76,7 @@
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.os.UserHandle;
+import android.service.voice.IVoiceInteractionSession;
 import android.util.EventLog;
 import android.util.Slog;
 import android.util.SparseArray;
@@ -87,6 +87,7 @@
 import android.view.InputEvent;
 import android.view.Surface;
 import com.android.internal.app.HeavyWeightSwitcherActivity;
+import com.android.internal.app.IVoiceInteractor;
 import com.android.internal.os.TransferPipe;
 import com.android.server.LocalServices;
 import com.android.server.am.ActivityManagerService.PendingActivityLaunch;
@@ -687,13 +688,14 @@
 
     void startHomeActivity(Intent intent, ActivityInfo aInfo) {
         moveHomeToTop();
-        startActivityLocked(null, intent, null, aInfo, null, null, 0, 0, 0, null, 0,
+        startActivityLocked(null, intent, null, aInfo, null, null, null, null, 0, 0, 0, null, 0,
                 null, false, null, null);
     }
 
     final int startActivityMayWait(IApplicationThread caller, int callingUid,
-            String callingPackage, Intent intent, String resolvedType, IBinder resultTo,
-            String resultWho, int requestCode, int startFlags, String profileFile,
+            String callingPackage, Intent intent, String resolvedType,
+            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
+            IBinder resultTo, String resultWho, int requestCode, int startFlags, String profileFile,
             ParcelFileDescriptor profileFd, WaitResult outResult, Configuration config,
             Bundle options, int userId, IActivityContainer iContainer) {
         // Refuse possible leaked file descriptors
@@ -802,7 +804,8 @@
                 }
             }
 
-            int res = startActivityLocked(caller, intent, resolvedType, aInfo, resultTo, resultWho,
+            int res = startActivityLocked(caller, intent, resolvedType, aInfo,
+                    voiceSession, voiceInteractor, resultTo, resultWho,
                     requestCode, callingPid, callingUid, callingPackage, startFlags, options,
                     componentSpecified, null, container);
 
@@ -918,7 +921,7 @@
                         theseOptions = null;
                     }
                     int res = startActivityLocked(caller, intent, resolvedTypes[i],
-                            aInfo, resultTo, null, -1, callingPid, callingUid, callingPackage,
+                            aInfo, null, null, resultTo, null, -1, callingPid, callingUid, callingPackage,
                             0, theseOptions, componentSpecified, outActivity, null);
                     if (res < 0) {
                         return res;
@@ -1034,8 +1037,8 @@
             app.thread.scheduleLaunchActivity(new Intent(r.intent), r.appToken,
                     System.identityHashCode(r), r.info,
                     new Configuration(mService.mConfiguration), r.compat,
-                    app.repProcState, r.icicle, results, newIntents, !andResume,
-                    mService.isNextTransitionForward(), profileFile, profileFd,
+                    r.task.voiceInteractor, app.repProcState, r.icicle, results, newIntents,
+                    !andResume, mService.isNextTransitionForward(), profileFile, profileFd,
                     profileAutoStop, options);
 
             if ((app.info.flags&ApplicationInfo.FLAG_CANT_SAVE_STATE) != 0) {
@@ -1143,8 +1146,9 @@
     }
 
     final int startActivityLocked(IApplicationThread caller,
-            Intent intent, String resolvedType, ActivityInfo aInfo, IBinder resultTo,
-            String resultWho, int requestCode,
+            Intent intent, String resolvedType, ActivityInfo aInfo,
+            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
+            IBinder resultTo, String resultWho, int requestCode,
             int callingPid, int callingUid, String callingPackage, int startFlags, Bundle options,
             boolean componentSpecified, ActivityRecord[] outActivity, ActivityContainer container) {
         int err = ActivityManager.START_SUCCESS;
@@ -1187,7 +1191,7 @@
         }
         ActivityStack resultStack = resultRecord == null ? null : resultRecord.task.stack;
 
-        int launchFlags = intent.getFlags();
+        final int launchFlags = intent.getFlags();
 
         if ((launchFlags&Intent.FLAG_ACTIVITY_FORWARD_RESULT) != 0
                 && sourceRecord != null) {
@@ -1232,6 +1236,38 @@
             err = ActivityManager.START_CLASS_NOT_FOUND;
         }
 
+        if (err == ActivityManager.START_SUCCESS && sourceRecord != null
+                && sourceRecord.task.voiceSession != null) {
+            // If this activity is being launched as part of a voice session, we need
+            // to ensure that it is safe to do so.  If the upcoming activity will also
+            // be part of the voice session, we can only launch it if it has explicitly
+            // said it supports the VOICE category, or it is a part of the calling app.
+            if ((launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) == 0
+                    && sourceRecord.info.applicationInfo.uid != aInfo.applicationInfo.uid) {
+                try {
+                    if (!AppGlobals.getPackageManager().activitySupportsIntent(intent.getComponent(),
+                            intent, resolvedType)) {
+                        err = ActivityManager.START_NOT_VOICE_COMPATIBLE;
+                    }
+                } catch (RemoteException e) {
+                    err = ActivityManager.START_NOT_VOICE_COMPATIBLE;
+                }
+            }
+        }
+
+        if (err == ActivityManager.START_SUCCESS && voiceSession != null) {
+            // If the caller is starting a new voice session, just make sure the target
+            // is actually allowing it to run this way.
+            try {
+                if (!AppGlobals.getPackageManager().activitySupportsIntent(intent.getComponent(),
+                        intent, resolvedType)) {
+                    err = ActivityManager.START_NOT_VOICE_COMPATIBLE;
+                }
+            } catch (RemoteException e) {
+                err = ActivityManager.START_NOT_VOICE_COMPATIBLE;
+            }
+        }
+
         if (err != ActivityManager.START_SUCCESS) {
             if (resultRecord != null) {
                 resultStack.sendActivityResultLocked(-1,
@@ -1305,8 +1341,8 @@
         }
 
         final ActivityStack stack = getFocusedStack();
-        if (stack.mResumedActivity == null
-                || stack.mResumedActivity.info.applicationInfo.uid != callingUid) {
+        if (voiceSession == null && (stack.mResumedActivity == null
+                || stack.mResumedActivity.info.applicationInfo.uid != callingUid)) {
             if (!mService.checkAppSwitchAllowedLocked(callingPid, callingUid, "Activity start")) {
                 PendingActivityLaunch pal =
                         new PendingActivityLaunch(r, sourceRecord, startFlags, stack);
@@ -1330,7 +1366,8 @@
 
         mService.doPendingActivityLaunchesLocked(false);
 
-        err = startActivityUncheckedLocked(r, sourceRecord, startFlags, true, options);
+        err = startActivityUncheckedLocked(r, sourceRecord, voiceSession, voiceInteractor,
+                startFlags, true, options);
 
         if (allPausedActivitiesComplete()) {
             // If someone asked to have the keyguard dismissed on the next
@@ -1410,8 +1447,9 @@
     }
 
     final int startActivityUncheckedLocked(ActivityRecord r,
-            ActivityRecord sourceRecord, int startFlags, boolean doResume,
-            Bundle options) {
+            ActivityRecord sourceRecord,
+            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor, int startFlags,
+            boolean doResume, Bundle options) {
         final Intent intent = r.intent;
         final int callingUid = r.launchedFromUid;
 
@@ -1755,7 +1793,7 @@
                 r.setTask(targetStack.createTaskRecord(getNextTaskId(),
                         newTaskInfo != null ? newTaskInfo : r.info,
                         newTaskIntent != null ? newTaskIntent : intent,
-                        true), null, true);
+                        voiceSession, voiceInteractor, true), null, true);
                 if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r + " in new task " +
                         r.task);
             } else {
@@ -1833,7 +1871,7 @@
             targetStack.moveToFront();
             ActivityRecord prev = targetStack.topActivity();
             r.setTask(prev != null ? prev.task
-                    : targetStack.createTaskRecord(getNextTaskId(), r.info, intent, true),
+                    : targetStack.createTaskRecord(getNextTaskId(), r.info, intent, null, null, true),
                     null, true);
             mWindowManager.moveTaskToTop(r.task.taskId);
             if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r
@@ -3104,7 +3142,7 @@
                     && "content".equals(intent.getData().getScheme())) {
                 mimeType = mService.getProviderMimeType(intent.getData(), userId);
             }
-            return startActivityMayWait(null, -1, null, intent, mimeType, null, null, 0, 0, null,
+            return startActivityMayWait(null, -1, null, intent, mimeType, null, null, null, null, 0, 0, null,
                     null, null, null, null, userId, this);
         }
 
@@ -3120,6 +3158,40 @@
                     null, 0, FORCE_NEW_TASK_FLAGS, FORCE_NEW_TASK_FLAGS, null, this);
         }
 
+        private void checkEmbeddedAllowedInner(Intent intent, String resolvedType) {
+            int userId = mService.handleIncomingUser(Binder.getCallingPid(),
+                    Binder.getCallingUid(), mCurrentUser, false, true, "ActivityContainer", null);
+            if (resolvedType == null) {
+                resolvedType = intent.getType();
+                if (resolvedType == null && intent.getData() != null
+                        && "content".equals(intent.getData().getScheme())) {
+                    resolvedType = mService.getProviderMimeType(intent.getData(), userId);
+                }
+            }
+            ActivityInfo aInfo = resolveActivity(intent, resolvedType, 0, null, null, userId);
+            if ((aInfo.flags & ActivityInfo.FLAG_ALLOW_EMBEDDED) == 0) {
+                throw new SecurityException(
+                        "Attempt to embed activity that has not set allowEmbedded=\"true\"");
+            }
+        }
+
+        /** Throw a SecurityException if allowEmbedded is not true */
+        @Override
+        public final void checkEmbeddedAllowed(Intent intent) {
+            checkEmbeddedAllowedInner(intent, null);
+        }
+
+        /** Throw a SecurityException if allowEmbedded is not true */
+        @Override
+        public final void checkEmbeddedAllowedIntentSender(IIntentSender intentSender) {
+            if (!(intentSender instanceof PendingIntentRecord)) {
+                throw new IllegalArgumentException("Bad PendingIntent object");
+            }
+            PendingIntentRecord pendingIntent = (PendingIntentRecord) intentSender;
+            checkEmbeddedAllowedInner(pendingIntent.key.requestIntent,
+                    pendingIntent.key.requestResolvedType);
+        }
+
         @Override
         public IBinder asBinder() {
             return this;
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index 80a219dd..68da54d 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -28,7 +28,9 @@
 import android.content.pm.ActivityInfo;
 import android.graphics.Bitmap;
 import android.os.UserHandle;
+import android.service.voice.IVoiceInteractionSession;
 import android.util.Slog;
+import com.android.internal.app.IVoiceInteractor;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -36,6 +38,8 @@
 final class TaskRecord extends ThumbnailHolder {
     final int taskId;       // Unique identifier for this task.
     final String affinity;  // The affinity name for this task, or null.
+    final IVoiceInteractionSession voiceSession;    // Voice interaction session driving task
+    final IVoiceInteractor voiceInteractor;         // Associated interactor to provide to app
     Intent intent;          // The original intent that started the task.
     Intent affinityIntent;  // Intent of affinity-moved activity that started this task.
     ComponentName origActivity; // The non-alias activity component of the intent.
@@ -64,9 +68,12 @@
      * Display.DEFAULT_DISPLAY. */
     boolean mOnTopOfHome = false;
 
-    TaskRecord(int _taskId, ActivityInfo info, Intent _intent) {
+    TaskRecord(int _taskId, ActivityInfo info, Intent _intent,
+            IVoiceInteractionSession _voiceSession, IVoiceInteractor _voiceInteractor) {
         taskId = _taskId;
         affinity = info.taskAffinity;
+        voiceSession = _voiceSession;
+        voiceInteractor = _voiceInteractor;
         setIntent(_intent, info);
     }
 
@@ -473,6 +480,12 @@
         if (affinity != null) {
             pw.print(prefix); pw.print("affinity="); pw.println(affinity);
         }
+        if (voiceSession != null || voiceInteractor != null) {
+            pw.print(prefix); pw.print("VOICE: session=0x");
+            pw.print(Integer.toHexString(System.identityHashCode(voiceSession)));
+            pw.print(" interactor=0x");
+            pw.println(Integer.toHexString(System.identityHashCode(voiceInteractor)));
+        }
         if (intent != null) {
             StringBuilder sb = new StringBuilder(128);
             sb.append(prefix); sb.append("intent={");
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecController.java b/services/core/java/com/android/server/hdmi/HdmiCecController.java
new file mode 100644
index 0000000..5f07108
--- /dev/null
+++ b/services/core/java/com/android/server/hdmi/HdmiCecController.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.hdmi;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+
+/**
+ * Manages HDMI-CEC command and behaviors. It converts user's command into CEC command
+ * and pass it to CEC HAL so that it sends message to other device. For incoming
+ * message it translates the message and delegates it to proper module.
+ *
+ * <p>It can be created only by {@link HdmiCecController#create}
+ *
+ * <p>Declared as package-private, accessed by {@link HdmiControlService} only.
+ */
+class HdmiCecController {
+    private static final String TAG = "HdmiCecController";
+
+    // Handler instance to process synchronous I/O (mainly send) message.
+    private Handler mIoHandler;
+
+    // Handler instance to process various messages coming from other CEC
+    // device or issued by internal state change.
+    private Handler mMessageHandler;
+
+    // Stores the pointer to the native implementation of the service that
+    // interacts with HAL.
+    private long mNativePtr;
+
+    // Private constructor.  Use HdmiCecController.create().
+    private HdmiCecController() {
+    }
+
+    /**
+     * A factory method to get {@link HdmiCecController}. If it fails to initialize
+     * inner device or has no device it will return {@code null}.
+     *
+     * <p>Declared as package-private, accessed by {@link HdmiControlService} only.
+     *
+     * @param ioLooper a Looper instance to handle IO (mainly send message) operation.
+     * @param messageHandler a message handler that processes a message coming from other
+     *                       CEC compatible device or callback of internal state change.
+     * @return {@link HdmiCecController} if device is initialized successfully. Otherwise,
+     *         returns {@code null}.
+     */
+    static HdmiCecController create(Looper ioLooper, Handler messageHandler) {
+        HdmiCecController handler = new HdmiCecController();
+        long nativePtr = nativeInit(handler);
+        if (nativePtr == 0L) {
+            handler = null;
+            return null;
+        }
+
+        handler.init(ioLooper, messageHandler, nativePtr);
+        return handler;
+    }
+
+    private void init(Looper ioLooper, Handler messageHandler, long nativePtr) {
+        mIoHandler = new Handler(ioLooper) {
+                @Override
+            public void handleMessage(Message msg) {
+                // TODO: Call native sendMessage.
+            }
+        };
+
+        mMessageHandler = messageHandler;
+        mNativePtr = nativePtr;
+    }
+
+    /**
+     * Called by native when an HDMI-CEC message arrived.
+     */
+    private void handleMessage(int srcAddress, int dstAddres, int opcode, byte[] params) {
+        // TODO: Translate message and delegate it to main message handler.
+    }
+
+    private static native long nativeInit(HdmiCecController handler);
+}
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
new file mode 100644
index 0000000..56c5b49
--- /dev/null
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -0,0 +1,70 @@
+/*
+ * 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.hdmi;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Message;
+import android.util.Slog;
+
+import com.android.server.SystemService;
+
+/**
+ * Provides a service for sending and processing HDMI control messages,
+ * HDMI-CEC and MHL control command, and providing the information on both standard.
+ */
+public final class HdmiControlService extends SystemService {
+    private static final String TAG = "HdmiControlService";
+
+    // A thread to handle synchronous IO of CEC and MHL control service.
+    // Since all of CEC and MHL HAL interfaces processed in short time (< 200ms)
+    // and sparse call it shares a thread to handle IO operations.
+    private final HandlerThread mIoThread = new HandlerThread("Hdmi Control Io Thread");
+
+    // Main handler class to handle incoming message from each controller.
+    private final Handler mHandler = new Handler() {
+        @Override
+        public void handleMessage(Message msg) {
+            // TODO: Add handler for each message type.
+        }
+    };
+
+    @Nullable
+    private HdmiCecController mCecController;
+
+    @Nullable
+    private HdmiMhlController mMhlController;
+
+    public HdmiControlService(Context context) {
+        super(context);
+    }
+
+    @Override
+    public void onStart() {
+        mCecController = HdmiCecController.create(mIoThread.getLooper(), mHandler);
+        if (mCecController == null) {
+            Slog.i(TAG, "Device does not support HDMI-CEC.");
+        }
+
+        mMhlController = HdmiMhlController.create(mIoThread.getLooper(), mHandler);
+        if (mMhlController == null) {
+            Slog.i(TAG, "Device does not support MHL-control.");
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/location/FlpHardwareProvider.java b/services/core/java/com/android/server/location/FlpHardwareProvider.java
index fab84a8..79f192d 100644
--- a/services/core/java/com/android/server/location/FlpHardwareProvider.java
+++ b/services/core/java/com/android/server/location/FlpHardwareProvider.java
@@ -60,6 +60,10 @@
     private static final int FLP_RESULT_ID_UNKNOWN = -5;
     private static final int FLP_RESULT_INVALID_GEOFENCE_TRANSITION = -6;
 
+    // FlpHal monitor status codes, they must be equal to the ones in fused_location.h
+    private static final int FLP_GEOFENCE_MONITOR_STATUS_UNAVAILABLE = 1<<0;
+    private static final int FLP_GEOFENCE_MONITOR_STATUS_AVAILABLE = 1<<1;
+
     public static FlpHardwareProvider getInstance(Context context) {
         if (sSingletonInstance == null) {
             sSingletonInstance = new FlpHardwareProvider(context);
@@ -141,6 +145,8 @@
             int transition,
             long timestamp,
             int sourcesUsed) {
+        // the transition Id does not require translation because the values in fused_location.h
+        // and GeofenceHardware are in sync
         getGeofenceHardwareSink().reportGeofenceTransition(
                 geofenceId,
                 updateLocationInformation(location),
@@ -157,9 +163,23 @@
             updatedLocation = updateLocationInformation(location);
         }
 
+        int monitorStatus;
+        switch (status) {
+            case FLP_GEOFENCE_MONITOR_STATUS_UNAVAILABLE:
+                monitorStatus = GeofenceHardware.MONITOR_CURRENTLY_UNAVAILABLE;
+                break;
+            case FLP_GEOFENCE_MONITOR_STATUS_AVAILABLE:
+                monitorStatus = GeofenceHardware.MONITOR_CURRENTLY_AVAILABLE;
+                break;
+            default:
+                Log.e(TAG, "Invalid FlpHal Geofence monitor status: " + status);
+                monitorStatus = GeofenceHardware.MONITOR_CURRENTLY_UNAVAILABLE;
+                break;
+        }
+
         getGeofenceHardwareSink().reportGeofenceMonitorStatus(
                 GeofenceHardware.MONITORING_TYPE_FUSED_HARDWARE,
-                status,
+                monitorStatus,
                 updatedLocation,
                 source);
     }
diff --git a/services/core/java/com/android/server/notification/ConditionProviders.java b/services/core/java/com/android/server/notification/ConditionProviders.java
new file mode 100644
index 0000000..29af433
--- /dev/null
+++ b/services/core/java/com/android/server/notification/ConditionProviders.java
@@ -0,0 +1,257 @@
+/**
+ * Copyright (c) 2014, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.notification;
+
+import android.content.Context;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.IInterface;
+import android.os.RemoteException;
+import android.provider.Settings;
+import android.provider.Settings.Global;
+import android.service.notification.Condition;
+import android.service.notification.ConditionProviderService;
+import android.service.notification.IConditionListener;
+import android.service.notification.IConditionProvider;
+import android.util.ArrayMap;
+import android.util.Slog;
+
+import com.android.internal.R;
+
+import libcore.util.Objects;
+
+import java.io.PrintWriter;
+import java.util.Arrays;
+
+public class ConditionProviders extends ManagedServices {
+
+    private final ZenModeHelper mZenModeHelper;
+    private final ArrayMap<IBinder, IConditionListener> mListeners
+            = new ArrayMap<IBinder, IConditionListener>();
+    private final ArrayMap<Uri, ManagedServiceInfo> mConditions
+            = new ArrayMap<Uri, ManagedServiceInfo>();
+
+    private Uri mCurrentConditionId;
+
+    public ConditionProviders(Context context, Handler handler,
+            UserProfiles userProfiles, ZenModeHelper zenModeHelper) {
+        super(context, handler, new Object(), userProfiles);
+        mZenModeHelper = zenModeHelper;
+        mZenModeHelper.addCallback(new ZenModeHelperCallback());
+    }
+
+    @Override
+    protected Config getConfig() {
+        Config c = new Config();
+        c.caption = "condition provider";
+        c.serviceInterface = ConditionProviderService.SERVICE_INTERFACE;
+        c.secureSettingName = Settings.Secure.ENABLED_CONDITION_PROVIDERS;
+        c.bindPermission = android.Manifest.permission.BIND_CONDITION_PROVIDER_SERVICE;
+        c.settingsAction = Settings.ACTION_CONDITION_PROVIDER_SETTINGS;
+        c.clientLabel = R.string.condition_provider_service_binding_label;
+        return c;
+    }
+
+    @Override
+    public void dump(PrintWriter pw) {
+        super.dump(pw);
+        synchronized(mMutex) {
+            pw.print("    mCurrentConditionId="); pw.println(mCurrentConditionId);
+            pw.print("    mListeners("); pw.print(mListeners.size()); pw.println("):");
+            for (int i = 0; i < mListeners.size(); i++) {
+                pw.print("      "); pw.println(mListeners.keyAt(i));
+            }
+            pw.print("    mConditions("); pw.print(mConditions.size()); pw.println("):");
+            for (int i = 0; i < mConditions.size(); i++) {
+                pw.print("      "); pw.print(mConditions.keyAt(i));
+                final ManagedServiceInfo info = mConditions.valueAt(i);
+                pw.print(" -> "); pw.print(info.component);
+                if (!mServices.contains(info)) {
+                    pw.print(" (orphan)");
+                }
+                pw.println();
+            }
+        }
+    }
+
+    @Override
+    protected IInterface asInterface(IBinder binder) {
+        return IConditionProvider.Stub.asInterface(binder);
+    }
+
+    @Override
+    protected void onServiceAdded(IInterface service) {
+        Slog.d(TAG, "onServiceAdded " + service);
+        final IConditionProvider provider = (IConditionProvider) service;
+        try {
+            provider.onConnected();
+        } catch (RemoteException e) {
+            // we tried
+        }
+    }
+
+    @Override
+    protected void onServiceRemovedLocked(ManagedServiceInfo removed) {
+        if (removed == null) return;
+        for (int i = mConditions.size() - 1; i >= 0; i--) {
+            if (removed.equals(mConditions.valueAt(i))) {
+                mConditions.removeAt(i);
+            }
+        }
+    }
+
+    public ManagedServiceInfo checkServiceToken(IConditionProvider provider) {
+        synchronized(mMutex) {
+            return checkServiceTokenLocked(provider);
+        }
+    }
+
+    public void requestZenModeConditions(IConditionListener callback, boolean requested) {
+        synchronized(mMutex) {
+            if (DEBUG) Slog.d(TAG, "requestZenModeConditions callback=" + callback
+                    + " requested=" + requested);
+            if (callback == null) return;
+            if (requested) {
+                mListeners.put(callback.asBinder(), callback);
+                requestConditionsLocked(Condition.FLAG_RELEVANT_NOW);
+            } else {
+                mListeners.remove(callback.asBinder());
+                if (mListeners.isEmpty()) {
+                    requestConditionsLocked(0);
+                }
+            }
+        }
+    }
+
+    public void notifyConditions(String pkg, ManagedServiceInfo info, Condition[] conditions) {
+        synchronized(mMutex) {
+            if (DEBUG) Slog.d(TAG, "notifyConditions pkg=" + pkg + " info=" + info + " conditions="
+                    + (conditions == null ? null : Arrays.asList(conditions)));
+            if (conditions == null || conditions.length == 0) return;
+            final int N = conditions.length;
+            boolean valid = true;
+            for (int i = 0; i < N; i++) {
+                final Uri id = conditions[i].id;
+                if (!Condition.isValidId(id, pkg)) {
+                    Slog.w(TAG, "Ignoring conditions from " + pkg + " for invalid id: " + id);
+                    valid = false;
+                }
+            }
+            if (!valid) return;
+
+            for (int i = 0; i < N; i++) {
+                mConditions.put(conditions[i].id, info);
+            }
+            for (IConditionListener listener : mListeners.values()) {
+                try {
+                    listener.onConditionsReceived(conditions);
+                } catch (RemoteException e) {
+                    Slog.w(TAG, "Error sending conditions to listener " + listener, e);
+                }
+            }
+            if (mCurrentConditionId != null) {
+                for (int i = 0; i < N; i++) {
+                    final Condition c = conditions[i];
+                    if (!c.id.equals(mCurrentConditionId)) continue;
+                    if (c.state == Condition.STATE_TRUE || c.state == Condition.STATE_ERROR) {
+                        triggerExitLocked(c.state == Condition.STATE_ERROR);
+                        return;
+                    }
+                }
+            }
+        }
+    }
+
+    private void triggerExitLocked(boolean error) {
+        if (error) {
+            Slog.w(TAG, "Zen mode exit condition failed");
+        } else if (DEBUG) {
+            Slog.d(TAG, "Zen mode exit condition triggered");
+        }
+        mZenModeHelper.setZenMode(Settings.Global.ZEN_MODE_OFF);
+        unsubscribeLocked(mCurrentConditionId);
+        mCurrentConditionId = null;
+    }
+
+    public void setZenModeCondition(Uri conditionId) {
+        synchronized(mMutex) {
+            if (DEBUG) Slog.d(TAG, "setZenModeCondition " + conditionId);
+            if (Objects.equal(mCurrentConditionId, conditionId)) return;
+
+            if (mCurrentConditionId != null) {
+                unsubscribeLocked(mCurrentConditionId);
+            }
+            if (conditionId != null) {
+                final ManagedServiceInfo info = mConditions.get(conditionId);
+                final IConditionProvider provider = provider(info);
+                if (provider == null) return;
+                try {
+                    provider.onSubscribe(conditionId);
+                } catch (RemoteException e) {
+                    Slog.w(TAG, "Error subscribing to " + conditionId
+                            + " from " + info.component, e);
+                }
+            }
+            mCurrentConditionId = conditionId;
+        }
+    }
+
+    private void unsubscribeLocked(Uri conditionId) {
+        final ManagedServiceInfo info = mConditions.get(mCurrentConditionId);
+        final IConditionProvider provider = provider(info);
+        if (provider == null) return;
+        try {
+            provider.onUnsubscribe(conditionId);
+        } catch (RemoteException e) {
+            Slog.w(TAG, "Error unsubscribing to " + conditionId + " from " + info.component, e);
+        }
+    }
+
+    private static IConditionProvider provider(ManagedServiceInfo info) {
+        return info == null ? null : (IConditionProvider) info.service;
+    }
+
+    private void requestConditionsLocked(int flags) {
+        for (ManagedServiceInfo info : mServices) {
+            final IConditionProvider provider = provider(info);
+            if (provider == null) continue;
+            try {
+                provider.onRequestConditions(flags);
+            } catch (RemoteException e) {
+                Slog.w(TAG, "Error requesting conditions from " + info.component, e);
+            }
+        }
+    }
+
+    private class ZenModeHelperCallback extends ZenModeHelper.Callback {
+        @Override
+        void onZenModeChanged() {
+            final int mode = mZenModeHelper.getZenMode();
+            if (mode == Global.ZEN_MODE_OFF) {
+                synchronized (mMutex) {
+                    if (mCurrentConditionId != null) {
+                        if (DEBUG) Slog.d(TAG, "Zen mode off, forcing unsubscribe from "
+                                + mCurrentConditionId);
+                        unsubscribeLocked(mCurrentConditionId);
+                        mCurrentConditionId = null;
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
new file mode 100644
index 0000000..0621f58
--- /dev/null
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -0,0 +1,621 @@
+/**
+ * Copyright (c) 2014, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.notification;
+
+import android.app.ActivityManager;
+import android.app.PendingIntent;
+import android.content.ComponentName;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.content.pm.UserInfo;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.IInterface;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.provider.Settings;
+import android.text.TextUtils;
+import android.util.ArraySet;
+import android.util.Slog;
+import android.util.SparseArray;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Manages the lifecycle of application-provided services bound by system server.
+ *
+ * Services managed by this helper must have:
+ *  - An associated system settings value with a list of enabled component names.
+ *  - A well-known action for services to use in their intent-filter.
+ *  - A system permission for services to require in order to ensure system has exclusive binding.
+ *  - A settings page for user configuration of enabled services, and associated intent action.
+ *  - A remote interface definition (aidl) provided by the service used for communication.
+ */
+abstract public class ManagedServices {
+    protected final String TAG = getClass().getSimpleName();
+    protected static final boolean DEBUG = true;
+
+    private static final String ENABLED_SERVICES_SEPARATOR = ":";
+
+    private final Context mContext;
+    protected final Object mMutex;
+    private final UserProfiles mUserProfiles;
+    private final SettingsObserver mSettingsObserver;
+    private final Config mConfig;
+
+    // contains connections to all connected services, including app services
+    // and system services
+    protected final ArrayList<ManagedServiceInfo> mServices = new ArrayList<ManagedServiceInfo>();
+    // things that will be put into mServices as soon as they're ready
+    private final ArrayList<String> mServicesBinding = new ArrayList<String>();
+    // lists the component names of all enabled (and therefore connected)
+    // app services for current profiles.
+    private ArraySet<ComponentName> mEnabledServicesForCurrentProfiles
+            = new ArraySet<ComponentName>();
+    // Just the packages from mEnabledServicesForCurrentProfiles
+    private ArraySet<String> mEnabledServicesPackageNames = new ArraySet<String>();
+
+    public ManagedServices(Context context, Handler handler, Object mutex,
+            UserProfiles userProfiles) {
+        mContext = context;
+        mMutex = mutex;
+        mUserProfiles = userProfiles;
+        mConfig = getConfig();
+        mSettingsObserver = new SettingsObserver(handler);
+    }
+
+    abstract protected Config getConfig();
+
+    private String getCaption() {
+        return mConfig.caption;
+    }
+
+    abstract protected IInterface asInterface(IBinder binder);
+
+    abstract protected void onServiceAdded(IInterface service);
+
+    protected void onServiceRemovedLocked(ManagedServiceInfo removed) { }
+
+    private ManagedServiceInfo newServiceInfo(IInterface service,
+            ComponentName component, int userid, boolean isSystem, ServiceConnection connection,
+            int targetSdkVersion) {
+        return new ManagedServiceInfo(service, component, userid, isSystem, connection,
+                targetSdkVersion);
+    }
+
+    public void onBootPhaseAppsCanStart() {
+        mSettingsObserver.observe();
+    }
+
+    public void dump(PrintWriter pw) {
+        pw.println("    All " + getCaption() + "s (" + mEnabledServicesForCurrentProfiles.size()
+                + ") enabled for current profiles:");
+        for (ComponentName cmpt : mEnabledServicesForCurrentProfiles) {
+            pw.println("      " + cmpt);
+        }
+
+        pw.println("    Live " + getCaption() + "s (" + mServices.size() + "):");
+        for (ManagedServiceInfo info : mServices) {
+            pw.println("      " + info.component
+                    + " (user " + info.userid + "): " + info.service
+                    + (info.isSystem?" SYSTEM":""));
+        }
+    }
+
+    public void onPackagesChanged(boolean queryReplace, String[] pkgList) {
+        if (DEBUG) Slog.d(TAG, "onPackagesChanged queryReplace=" + queryReplace
+                + " pkgList=" + (pkgList == null ? null : Arrays.asList(pkgList))
+                + " mEnabledServicesPackageNames=" + mEnabledServicesPackageNames);
+        boolean anyServicesInvolved = false;
+        if (pkgList != null && (pkgList.length > 0)) {
+            for (String pkgName : pkgList) {
+                if (mEnabledServicesPackageNames.contains(pkgName)) {
+                    anyServicesInvolved = true;
+                }
+            }
+        }
+
+        if (anyServicesInvolved) {
+            // if we're not replacing a package, clean up orphaned bits
+            if (!queryReplace) {
+                disableNonexistentServices();
+            }
+            // make sure we're still bound to any of our services who may have just upgraded
+            rebindServices();
+        }
+    }
+
+    public ManagedServiceInfo checkServiceTokenLocked(IInterface service) {
+        checkNotNull(service);
+        final IBinder token = service.asBinder();
+        final int N = mServices.size();
+        for (int i=0; i<N; i++) {
+            final ManagedServiceInfo info = mServices.get(i);
+            if (info.service.asBinder() == token) return info;
+        }
+        throw new SecurityException("Disallowed call from unknown " + getCaption() + ": "
+                + service);
+    }
+
+    public void unregisterService(IInterface service, int userid) {
+        checkNotNull(service);
+        // no need to check permissions; if your service binder is in the list,
+        // that's proof that you had permission to add it in the first place
+        unregisterServiceImpl(service, userid);
+    }
+
+    public void registerService(IInterface service, ComponentName component, int userid) {
+        checkNotNull(service);
+        registerServiceImpl(service, component, userid);
+    }
+
+    /**
+     * Remove access for any services that no longer exist.
+     */
+    private void disableNonexistentServices() {
+        int[] userIds = mUserProfiles.getCurrentProfileIds();
+        final int N = userIds.length;
+        for (int i = 0 ; i < N; ++i) {
+            disableNonexistentServices(userIds[i]);
+        }
+    }
+
+    private void disableNonexistentServices(int userId) {
+        String flatIn = Settings.Secure.getStringForUser(
+                mContext.getContentResolver(),
+                mConfig.secureSettingName,
+                userId);
+        if (!TextUtils.isEmpty(flatIn)) {
+            if (DEBUG) Slog.v(TAG, "flat before: " + flatIn);
+            PackageManager pm = mContext.getPackageManager();
+            List<ResolveInfo> installedServices = pm.queryIntentServicesAsUser(
+                    new Intent(mConfig.serviceInterface),
+                    PackageManager.GET_SERVICES | PackageManager.GET_META_DATA,
+                    userId);
+            if (DEBUG) Slog.v(TAG, mConfig.serviceInterface + " services: " + installedServices);
+            Set<ComponentName> installed = new ArraySet<ComponentName>();
+            for (int i = 0, count = installedServices.size(); i < count; i++) {
+                ResolveInfo resolveInfo = installedServices.get(i);
+                ServiceInfo info = resolveInfo.serviceInfo;
+
+                if (!mConfig.bindPermission.equals(info.permission)) {
+                    Slog.w(TAG, "Skipping " + getCaption() + " service "
+                            + info.packageName + "/" + info.name
+                            + ": it does not require the permission "
+                            + mConfig.bindPermission);
+                    continue;
+                }
+                installed.add(new ComponentName(info.packageName, info.name));
+            }
+
+            String flatOut = "";
+            if (!installed.isEmpty()) {
+                String[] enabled = flatIn.split(ENABLED_SERVICES_SEPARATOR);
+                ArrayList<String> remaining = new ArrayList<String>(enabled.length);
+                for (int i = 0; i < enabled.length; i++) {
+                    ComponentName enabledComponent = ComponentName.unflattenFromString(enabled[i]);
+                    if (installed.contains(enabledComponent)) {
+                        remaining.add(enabled[i]);
+                    }
+                }
+                flatOut = TextUtils.join(ENABLED_SERVICES_SEPARATOR, remaining);
+            }
+            if (DEBUG) Slog.v(TAG, "flat after: " + flatOut);
+            if (!flatIn.equals(flatOut)) {
+                Settings.Secure.putStringForUser(mContext.getContentResolver(),
+                        mConfig.secureSettingName,
+                        flatOut, userId);
+            }
+        }
+    }
+
+    /**
+     * Called whenever packages change, the user switches, or the secure setting
+     * is altered. (For example in response to USER_SWITCHED in our broadcast receiver)
+     */
+    private void rebindServices() {
+        if (DEBUG) Slog.d(TAG, "rebindServices");
+        final int[] userIds = mUserProfiles.getCurrentProfileIds();
+        final int nUserIds = userIds.length;
+
+        final SparseArray<String> flat = new SparseArray<String>();
+
+        for (int i = 0; i < nUserIds; ++i) {
+            flat.put(userIds[i], Settings.Secure.getStringForUser(
+                    mContext.getContentResolver(),
+                    mConfig.secureSettingName,
+                    userIds[i]));
+        }
+
+        ManagedServiceInfo[] toRemove = new ManagedServiceInfo[mServices.size()];
+        final SparseArray<ArrayList<ComponentName>> toAdd
+                = new SparseArray<ArrayList<ComponentName>>();
+
+        synchronized (mMutex) {
+            // unbind and remove all existing services
+            toRemove = mServices.toArray(toRemove);
+
+            final ArraySet<ComponentName> newEnabled = new ArraySet<ComponentName>();
+            final ArraySet<String> newPackages = new ArraySet<String>();
+
+            for (int i = 0; i < nUserIds; ++i) {
+                final ArrayList<ComponentName> add = new ArrayList<ComponentName>();
+                toAdd.put(userIds[i], add);
+
+                // decode the list of components
+                String toDecode = flat.get(userIds[i]);
+                if (toDecode != null) {
+                    String[] components = toDecode.split(ENABLED_SERVICES_SEPARATOR);
+                    for (int j = 0; j < components.length; j++) {
+                        final ComponentName component
+                                = ComponentName.unflattenFromString(components[j]);
+                        if (component != null) {
+                            newEnabled.add(component);
+                            add.add(component);
+                            newPackages.add(component.getPackageName());
+                        }
+                    }
+
+                }
+            }
+            mEnabledServicesForCurrentProfiles = newEnabled;
+            mEnabledServicesPackageNames = newPackages;
+        }
+
+        for (ManagedServiceInfo info : toRemove) {
+            final ComponentName component = info.component;
+            final int oldUser = info.userid;
+            Slog.v(TAG, "disabling " + getCaption() + " for user "
+                    + oldUser + ": " + component);
+            unregisterService(component, info.userid);
+        }
+
+        for (int i = 0; i < nUserIds; ++i) {
+            final ArrayList<ComponentName> add = toAdd.get(userIds[i]);
+            final int N = add.size();
+            for (int j = 0; j < N; j++) {
+                final ComponentName component = add.get(j);
+                Slog.v(TAG, "enabling " + getCaption() + " for user " + userIds[i] + ": "
+                        + component);
+                registerService(component, userIds[i]);
+            }
+        }
+    }
+
+    /**
+     * Version of registerService that takes the name of a service component to bind to.
+     */
+    private void registerService(final ComponentName name, final int userid) {
+        if (DEBUG) Slog.v(TAG, "registerService: " + name + " u=" + userid);
+
+        synchronized (mMutex) {
+            final String servicesBindingTag = name.toString() + "/" + userid;
+            if (mServicesBinding.contains(servicesBindingTag)) {
+                // stop registering this thing already! we're working on it
+                return;
+            }
+            mServicesBinding.add(servicesBindingTag);
+
+            final int N = mServices.size();
+            for (int i=N-1; i>=0; i--) {
+                final ManagedServiceInfo info = mServices.get(i);
+                if (name.equals(info.component)
+                        && info.userid == userid) {
+                    // cut old connections
+                    if (DEBUG) Slog.v(TAG, "    disconnecting old " + getCaption() + ": "
+                            + info.service);
+                    removeServiceLocked(i);
+                    if (info.connection != null) {
+                        mContext.unbindService(info.connection);
+                    }
+                }
+            }
+
+            Intent intent = new Intent(mConfig.serviceInterface);
+            intent.setComponent(name);
+
+            intent.putExtra(Intent.EXTRA_CLIENT_LABEL, mConfig.clientLabel);
+
+            final PendingIntent pendingIntent = PendingIntent.getActivity(
+                    mContext, 0, new Intent(mConfig.settingsAction), 0);
+            intent.putExtra(Intent.EXTRA_CLIENT_INTENT, pendingIntent);
+
+            ApplicationInfo appInfo = null;
+            try {
+                appInfo = mContext.getPackageManager().getApplicationInfo(
+                        name.getPackageName(), 0);
+            } catch (NameNotFoundException e) {
+                // Ignore if the package doesn't exist we won't be able to bind to the service.
+            }
+            final int targetSdkVersion =
+                    appInfo != null ? appInfo.targetSdkVersion : Build.VERSION_CODES.BASE;
+
+            try {
+                if (DEBUG) Slog.v(TAG, "binding: " + intent);
+                if (!mContext.bindServiceAsUser(intent,
+                        new ServiceConnection() {
+                            IInterface mService;
+
+                            @Override
+                            public void onServiceConnected(ComponentName name, IBinder binder) {
+                                boolean added = false;
+                                synchronized (mMutex) {
+                                    mServicesBinding.remove(servicesBindingTag);
+                                    try {
+                                        mService = asInterface(binder);
+                                        ManagedServiceInfo info = newServiceInfo(mService, name,
+                                                userid, false /*isSystem*/, this, targetSdkVersion);
+                                        binder.linkToDeath(info, 0);
+                                        added = mServices.add(info);
+                                    } catch (RemoteException e) {
+                                        // already dead
+                                    }
+                                }
+                                if (added) {
+                                    onServiceAdded(mService);
+                                }
+                            }
+
+                            @Override
+                            public void onServiceDisconnected(ComponentName name) {
+                                Slog.v(TAG, getCaption() + " connection lost: " + name);
+                            }
+                        },
+                        Context.BIND_AUTO_CREATE,
+                        new UserHandle(userid)))
+                {
+                    mServicesBinding.remove(servicesBindingTag);
+                    Slog.w(TAG, "Unable to bind " + getCaption() + " service: " + intent);
+                    return;
+                }
+            } catch (SecurityException ex) {
+                Slog.e(TAG, "Unable to bind " + getCaption() + " service: " + intent, ex);
+                return;
+            }
+        }
+    }
+
+    /**
+     * Remove a service for the given user by ComponentName
+     */
+    private void unregisterService(ComponentName name, int userid) {
+        synchronized (mMutex) {
+            final int N = mServices.size();
+            for (int i=N-1; i>=0; i--) {
+                final ManagedServiceInfo info = mServices.get(i);
+                if (name.equals(info.component)
+                        && info.userid == userid) {
+                    removeServiceLocked(i);
+                    if (info.connection != null) {
+                        try {
+                            mContext.unbindService(info.connection);
+                        } catch (IllegalArgumentException ex) {
+                            // something happened to the service: we think we have a connection
+                            // but it's bogus.
+                            Slog.e(TAG, getCaption() + " " + name + " could not be unbound: " + ex);
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Removes a service from the list but does not unbind
+     *
+     * @return the removed service.
+     */
+    private ManagedServiceInfo removeServiceImpl(IInterface service, final int userid) {
+        if (DEBUG) Slog.d(TAG, "removeServiceImpl service=" + service + " u=" + userid);
+        ManagedServiceInfo serviceInfo = null;
+        synchronized (mMutex) {
+            final int N = mServices.size();
+            for (int i=N-1; i>=0; i--) {
+                final ManagedServiceInfo info = mServices.get(i);
+                if (info.service.asBinder() == service.asBinder()
+                        && info.userid == userid) {
+                    if (DEBUG) Slog.d(TAG, "Removing active service " + info.component);
+                    serviceInfo = removeServiceLocked(i);
+                }
+            }
+        }
+        return serviceInfo;
+    }
+
+    private ManagedServiceInfo removeServiceLocked(int i) {
+        final ManagedServiceInfo info = mServices.remove(i);
+        onServiceRemovedLocked(info);
+        return info;
+    }
+
+    private void checkNotNull(IInterface service) {
+        if (service == null) {
+            throw new IllegalArgumentException(getCaption() + " must not be null");
+        }
+    }
+
+    private void registerServiceImpl(final IInterface service,
+            final ComponentName component, final int userid) {
+        synchronized (mMutex) {
+            try {
+                ManagedServiceInfo info = newServiceInfo(service, component, userid,
+                        true /*isSystem*/, null, Build.VERSION_CODES.L);
+                service.asBinder().linkToDeath(info, 0);
+                mServices.add(info);
+            } catch (RemoteException e) {
+                // already dead
+            }
+        }
+    }
+
+    /**
+     * Removes a service from the list and unbinds.
+     */
+    private void unregisterServiceImpl(IInterface service, int userid) {
+        ManagedServiceInfo info = removeServiceImpl(service, userid);
+        if (info != null && info.connection != null) {
+            mContext.unbindService(info.connection);
+        }
+    }
+
+    private class SettingsObserver extends ContentObserver {
+        private final Uri mSecureSettingsUri = Settings.Secure.getUriFor(mConfig.secureSettingName);
+
+        private SettingsObserver(Handler handler) {
+            super(handler);
+        }
+
+        private void observe() {
+            ContentResolver resolver = mContext.getContentResolver();
+            resolver.registerContentObserver(mSecureSettingsUri,
+                    false, this, UserHandle.USER_ALL);
+            update(null);
+        }
+
+        @Override
+        public void onChange(boolean selfChange, Uri uri) {
+            update(uri);
+        }
+
+        private void update(Uri uri) {
+            if (uri == null || mSecureSettingsUri.equals(uri)) {
+                rebindServices();
+            }
+        }
+    }
+
+    public class ManagedServiceInfo implements IBinder.DeathRecipient {
+        public IInterface service;
+        public ComponentName component;
+        public int userid;
+        public boolean isSystem;
+        public ServiceConnection connection;
+        public int targetSdkVersion;
+
+        public ManagedServiceInfo(IInterface service, ComponentName component,
+                int userid, boolean isSystem, ServiceConnection connection, int targetSdkVersion) {
+            this.service = service;
+            this.component = component;
+            this.userid = userid;
+            this.isSystem = isSystem;
+            this.connection = connection;
+            this.targetSdkVersion = targetSdkVersion;
+        }
+
+        @Override
+        public String toString() {
+            return new StringBuilder("ManagedServiceInfo[")
+                    .append("component=").append(component)
+                    .append(",userid=").append(userid)
+                    .append(",isSystem=").append(isSystem)
+                    .append(",targetSdkVersion=").append(targetSdkVersion)
+                    .append(",connection=").append(connection == null ? null : "<connection>")
+                    .append(",service=").append(service)
+                    .append(']').toString();
+        }
+
+        public boolean enabledAndUserMatches(int nid) {
+            if (!isEnabledForCurrentProfiles()) {
+                return false;
+            }
+            if (this.userid == UserHandle.USER_ALL) return true;
+            if (nid == UserHandle.USER_ALL || nid == this.userid) return true;
+            return supportsProfiles() && mUserProfiles.isCurrentProfile(nid);
+        }
+
+        public boolean supportsProfiles() {
+            return targetSdkVersion >= Build.VERSION_CODES.L;
+        }
+
+        @Override
+        public void binderDied() {
+            if (DEBUG) Slog.d(TAG, "binderDied");
+            // Remove the service, but don't unbind from the service. The system will bring the
+            // service back up, and the onServiceConnected handler will readd the service with the
+            // new binding. If this isn't a bound service, and is just a registered
+            // service, just removing it from the list is all we need to do anyway.
+            removeServiceImpl(this.service, this.userid);
+        }
+
+        /** convenience method for looking in mEnabledServicesForCurrentProfiles */
+        public boolean isEnabledForCurrentProfiles() {
+            if (this.isSystem) return true;
+            if (this.connection == null) return false;
+            return mEnabledServicesForCurrentProfiles.contains(this.component);
+        }
+    }
+
+    public static class UserProfiles {
+        // Profiles of the current user.
+        private final SparseArray<UserInfo> mCurrentProfiles = new SparseArray<UserInfo>();
+
+        public void updateCache(Context context) {
+            UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
+            if (userManager != null) {
+                int currentUserId = ActivityManager.getCurrentUser();
+                List<UserInfo> profiles = userManager.getProfiles(currentUserId);
+                synchronized (mCurrentProfiles) {
+                    mCurrentProfiles.clear();
+                    for (UserInfo user : profiles) {
+                        mCurrentProfiles.put(user.id, user);
+                    }
+                }
+            }
+        }
+
+        public int[] getCurrentProfileIds() {
+            synchronized (mCurrentProfiles) {
+                int[] users = new int[mCurrentProfiles.size()];
+                final int N = mCurrentProfiles.size();
+                for (int i = 0; i < N; ++i) {
+                    users[i] = mCurrentProfiles.keyAt(i);
+                }
+                return users;
+            }
+        }
+
+        public boolean isCurrentProfile(int userId) {
+            synchronized (mCurrentProfiles) {
+                return mCurrentProfiles.get(userId) != null;
+            }
+        }
+    }
+
+    protected static class Config {
+        String caption;
+        String serviceInterface;
+        String secureSettingName;
+        String bindPermission;
+        String settingsAction;
+        int clientLabel;
+    }
+}
diff --git a/services/core/java/com/android/server/notification/NotificationListeners.java b/services/core/java/com/android/server/notification/NotificationListeners.java
deleted file mode 100644
index 91d2f98..0000000
--- a/services/core/java/com/android/server/notification/NotificationListeners.java
+++ /dev/null
@@ -1,608 +0,0 @@
-/**
- * Copyright (c) 2014, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.notification;
-
-import android.app.PendingIntent;
-import android.content.ComponentName;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.content.pm.ServiceInfo;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.database.ContentObserver;
-import android.net.Uri;
-import android.os.Build;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.os.UserHandle;
-import android.provider.Settings;
-import android.service.notification.INotificationListener;
-import android.service.notification.NotificationListenerService;
-import android.service.notification.StatusBarNotification;
-import android.text.TextUtils;
-import android.util.ArraySet;
-import android.util.Log;
-import android.util.Slog;
-import android.util.SparseArray;
-
-import com.android.internal.R;
-import com.android.server.notification.NotificationManagerService.UserProfiles;
-
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Set;
-
-public class NotificationListeners {
-    private static final String TAG = "NotificationListeners";
-    private static final boolean DBG = NotificationManagerService.DBG;
-
-    private static final String ENABLED_NOTIFICATION_LISTENERS_SEPARATOR = ":";
-
-    private final Context mContext;
-    private final Handler mHandler;
-    private final Object mMutex;
-    private final UserProfiles mUserProfiles;
-    private final SettingsObserver mSettingsObserver;
-
-    // contains connections to all connected listeners, including app services
-    // and system listeners
-    private final ArrayList<NotificationListenerInfo> mListeners
-            = new ArrayList<NotificationListenerInfo>();
-    // things that will be put into mListeners as soon as they're ready
-    private final ArrayList<String> mServicesBinding = new ArrayList<String>();
-    // lists the component names of all enabled (and therefore connected) listener
-    // app services for current profiles.
-    private ArraySet<ComponentName> mEnabledListenersForCurrentProfiles
-            = new ArraySet<ComponentName>();
-    // Just the packages from mEnabledListenersForCurrentProfiles
-    private ArraySet<String> mEnabledListenerPackageNames = new ArraySet<String>();
-
-    public NotificationListeners(Context context, Handler handler, Object mutex,
-            UserProfiles userProfiles) {
-        mContext = context;
-        mHandler = handler;
-        mMutex = mutex;
-        mUserProfiles = userProfiles;
-        mSettingsObserver = new SettingsObserver(mHandler);
-    }
-
-    public void onBootPhaseAppsCanStart() {
-        mSettingsObserver.observe();
-    }
-
-    protected void onServiceAdded(INotificationListener mListener) {
-        // for subclasses
-    }
-
-    public void dump(PrintWriter pw) {
-        pw.println("  Listeners (" + mEnabledListenersForCurrentProfiles.size()
-                + ") enabled for current profiles:");
-        for (ComponentName cmpt : mEnabledListenersForCurrentProfiles) {
-            pw.println("    " + cmpt);
-        }
-
-        pw.println("  Live listeners (" + mListeners.size() + "):");
-        for (NotificationListenerInfo info : mListeners) {
-            pw.println("    " + info.component
-                    + " (user " + info.userid + "): " + info.listener
-                    + (info.isSystem?" SYSTEM":""));
-        }
-    }
-
-    public void onPackagesChanged(boolean queryReplace, String[] pkgList) {
-        boolean anyListenersInvolved = false;
-        if (pkgList != null && (pkgList.length > 0)) {
-            for (String pkgName : pkgList) {
-                if (mEnabledListenerPackageNames.contains(pkgName)) {
-                    anyListenersInvolved = true;
-                }
-            }
-        }
-
-        if (anyListenersInvolved) {
-            // if we're not replacing a package, clean up orphaned bits
-            if (!queryReplace) {
-                disableNonexistentListeners();
-            }
-            // make sure we're still bound to any of our
-            // listeners who may have just upgraded
-            rebindListenerServices();
-        }
-    }
-
-    /**
-     * asynchronously notify all listeners about a new notification
-     */
-    public void notifyPostedLocked(StatusBarNotification sbn) {
-        // make a copy in case changes are made to the underlying Notification object
-        final StatusBarNotification sbnClone = sbn.clone();
-        for (final NotificationListenerInfo info : mListeners) {
-            mHandler.post(new Runnable() {
-                @Override
-                public void run() {
-                    info.notifyPostedIfUserMatch(sbnClone);
-                }
-            });
-        }
-    }
-
-    /**
-     * asynchronously notify all listeners about a removed notification
-     */
-    public void notifyRemovedLocked(StatusBarNotification sbn) {
-        // make a copy in case changes are made to the underlying Notification object
-        // NOTE: this copy is lightweight: it doesn't include heavyweight parts of the notification
-        final StatusBarNotification sbnLight = sbn.cloneLight();
-
-        for (final NotificationListenerInfo info : mListeners) {
-            mHandler.post(new Runnable() {
-                @Override
-                public void run() {
-                    info.notifyRemovedIfUserMatch(sbnLight);
-                }
-            });
-        }
-    }
-
-    public NotificationListenerInfo checkListenerTokenLocked(INotificationListener listener) {
-        checkNullListener(listener);
-        final IBinder token = listener.asBinder();
-        final int N = mListeners.size();
-        for (int i=0; i<N; i++) {
-            final NotificationListenerInfo info = mListeners.get(i);
-            if (info.listener.asBinder() == token) return info;
-        }
-        throw new SecurityException("Disallowed call from unknown listener: " + listener);
-    }
-
-    public void unregisterListener(INotificationListener listener, int userid) {
-        checkNullListener(listener);
-        // no need to check permissions; if your listener binder is in the list,
-        // that's proof that you had permission to add it in the first place
-        unregisterListenerImpl(listener, userid);
-    }
-
-    public void registerListener(INotificationListener listener,
-            ComponentName component, int userid) {
-        checkNullListener(listener);
-        registerListenerImpl(listener, component, userid);
-    }
-
-    /**
-     * Remove notification access for any services that no longer exist.
-     */
-    private void disableNonexistentListeners() {
-        int[] userIds = mUserProfiles.getCurrentProfileIds();
-        final int N = userIds.length;
-        for (int i = 0 ; i < N; ++i) {
-            disableNonexistentListeners(userIds[i]);
-        }
-    }
-
-    private void disableNonexistentListeners(int userId) {
-        String flatIn = Settings.Secure.getStringForUser(
-                mContext.getContentResolver(),
-                Settings.Secure.ENABLED_NOTIFICATION_LISTENERS,
-                userId);
-        if (!TextUtils.isEmpty(flatIn)) {
-            if (DBG) Slog.v(TAG, "flat before: " + flatIn);
-            PackageManager pm = mContext.getPackageManager();
-            List<ResolveInfo> installedServices = pm.queryIntentServicesAsUser(
-                    new Intent(NotificationListenerService.SERVICE_INTERFACE),
-                    PackageManager.GET_SERVICES | PackageManager.GET_META_DATA,
-                    userId);
-
-            Set<ComponentName> installed = new ArraySet<ComponentName>();
-            for (int i = 0, count = installedServices.size(); i < count; i++) {
-                ResolveInfo resolveInfo = installedServices.get(i);
-                ServiceInfo info = resolveInfo.serviceInfo;
-
-                if (!android.Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE.equals(
-                                info.permission)) {
-                    Slog.w(TAG, "Skipping notification listener service "
-                            + info.packageName + "/" + info.name
-                            + ": it does not require the permission "
-                            + android.Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE);
-                    continue;
-                }
-                installed.add(new ComponentName(info.packageName, info.name));
-            }
-
-            String flatOut = "";
-            if (!installed.isEmpty()) {
-                String[] enabled = flatIn.split(ENABLED_NOTIFICATION_LISTENERS_SEPARATOR);
-                ArrayList<String> remaining = new ArrayList<String>(enabled.length);
-                for (int i = 0; i < enabled.length; i++) {
-                    ComponentName enabledComponent = ComponentName.unflattenFromString(enabled[i]);
-                    if (installed.contains(enabledComponent)) {
-                        remaining.add(enabled[i]);
-                    }
-                }
-                flatOut = TextUtils.join(ENABLED_NOTIFICATION_LISTENERS_SEPARATOR, remaining);
-            }
-            if (DBG) Slog.v(TAG, "flat after: " + flatOut);
-            if (!flatIn.equals(flatOut)) {
-                Settings.Secure.putStringForUser(mContext.getContentResolver(),
-                        Settings.Secure.ENABLED_NOTIFICATION_LISTENERS,
-                        flatOut, userId);
-            }
-        }
-    }
-
-    /**
-     * Called whenever packages change, the user switches, or ENABLED_NOTIFICATION_LISTENERS
-     * is altered. (For example in response to USER_SWITCHED in our broadcast receiver)
-     */
-    private void rebindListenerServices() {
-        final int[] userIds = mUserProfiles.getCurrentProfileIds();
-        final int nUserIds = userIds.length;
-
-        final SparseArray<String> flat = new SparseArray<String>();
-
-        for (int i = 0; i < nUserIds; ++i) {
-            flat.put(userIds[i], Settings.Secure.getStringForUser(
-                    mContext.getContentResolver(),
-                    Settings.Secure.ENABLED_NOTIFICATION_LISTENERS,
-                    userIds[i]));
-        }
-
-        NotificationListenerInfo[] toRemove = new NotificationListenerInfo[mListeners.size()];
-        final SparseArray<ArrayList<ComponentName>> toAdd
-                = new SparseArray<ArrayList<ComponentName>>();
-
-        synchronized (mMutex) {
-            // unbind and remove all existing listeners
-            toRemove = mListeners.toArray(toRemove);
-
-            final ArraySet<ComponentName> newEnabled = new ArraySet<ComponentName>();
-            final ArraySet<String> newPackages = new ArraySet<String>();
-
-            for (int i = 0; i < nUserIds; ++i) {
-                final ArrayList<ComponentName> add = new ArrayList<ComponentName>();
-                toAdd.put(userIds[i], add);
-
-                // decode the list of components
-                String toDecode = flat.get(userIds[i]);
-                if (toDecode != null) {
-                    String[] components = toDecode.split(ENABLED_NOTIFICATION_LISTENERS_SEPARATOR);
-                    for (int j = 0; j < components.length; j++) {
-                        final ComponentName component
-                                = ComponentName.unflattenFromString(components[j]);
-                        if (component != null) {
-                            newEnabled.add(component);
-                            add.add(component);
-                            newPackages.add(component.getPackageName());
-                        }
-                    }
-
-                }
-            }
-            mEnabledListenersForCurrentProfiles = newEnabled;
-            mEnabledListenerPackageNames = newPackages;
-        }
-
-        for (NotificationListenerInfo info : toRemove) {
-            final ComponentName component = info.component;
-            final int oldUser = info.userid;
-            Slog.v(TAG, "disabling notification listener for user "
-                    + oldUser + ": " + component);
-            unregisterListenerService(component, info.userid);
-        }
-
-        for (int i = 0; i < nUserIds; ++i) {
-            final ArrayList<ComponentName> add = toAdd.get(userIds[i]);
-            final int N = add.size();
-            for (int j = 0; j < N; j++) {
-                final ComponentName component = add.get(j);
-                Slog.v(TAG, "enabling notification listener for user " + userIds[i] + ": "
-                        + component);
-                registerListenerService(component, userIds[i]);
-            }
-        }
-    }
-
-    /**
-     * Version of registerListener that takes the name of a
-     * {@link android.service.notification.NotificationListenerService} to bind to.
-     *
-     * This is the mechanism by which third parties may subscribe to notifications.
-     */
-    private void registerListenerService(final ComponentName name, final int userid) {
-        NotificationUtil.checkCallerIsSystem();
-
-        if (DBG) Slog.v(TAG, "registerListenerService: " + name + " u=" + userid);
-
-        synchronized (mMutex) {
-            final String servicesBindingTag = name.toString() + "/" + userid;
-            if (mServicesBinding.contains(servicesBindingTag)) {
-                // stop registering this thing already! we're working on it
-                return;
-            }
-            mServicesBinding.add(servicesBindingTag);
-
-            final int N = mListeners.size();
-            for (int i=N-1; i>=0; i--) {
-                final NotificationListenerInfo info = mListeners.get(i);
-                if (name.equals(info.component)
-                        && info.userid == userid) {
-                    // cut old connections
-                    if (DBG) Slog.v(TAG, "    disconnecting old listener: " + info.listener);
-                    mListeners.remove(i);
-                    if (info.connection != null) {
-                        mContext.unbindService(info.connection);
-                    }
-                }
-            }
-
-            Intent intent = new Intent(NotificationListenerService.SERVICE_INTERFACE);
-            intent.setComponent(name);
-
-            intent.putExtra(Intent.EXTRA_CLIENT_LABEL,
-                    R.string.notification_listener_binding_label);
-
-            final PendingIntent pendingIntent = PendingIntent.getActivity(
-                    mContext, 0, new Intent(Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS), 0);
-            intent.putExtra(Intent.EXTRA_CLIENT_INTENT, pendingIntent);
-
-            ApplicationInfo appInfo = null;
-            try {
-                appInfo = mContext.getPackageManager().getApplicationInfo(
-                        name.getPackageName(), 0);
-            } catch (NameNotFoundException e) {
-                // Ignore if the package doesn't exist we won't be able to bind to the service.
-            }
-            final int targetSdkVersion =
-                    appInfo != null ? appInfo.targetSdkVersion : Build.VERSION_CODES.BASE;
-
-            try {
-                if (DBG) Slog.v(TAG, "binding: " + intent);
-                if (!mContext.bindServiceAsUser(intent,
-                        new ServiceConnection() {
-                            INotificationListener mListener;
-
-                            @Override
-                            public void onServiceConnected(ComponentName name, IBinder service) {
-                                boolean added = false;
-                                synchronized (mMutex) {
-                                    mServicesBinding.remove(servicesBindingTag);
-                                    try {
-                                        mListener = INotificationListener.Stub.asInterface(service);
-                                        NotificationListenerInfo info
-                                                = new NotificationListenerInfo(
-                                                        mListener, name, userid, this,
-                                                        targetSdkVersion);
-                                        service.linkToDeath(info, 0);
-                                        added = mListeners.add(info);
-                                    } catch (RemoteException e) {
-                                        // already dead
-                                    }
-                                }
-                                if (added) {
-                                    onServiceAdded(mListener);
-                                }
-                            }
-
-                            @Override
-                            public void onServiceDisconnected(ComponentName name) {
-                                Slog.v(TAG, "notification listener connection lost: " + name);
-                            }
-                        },
-                        Context.BIND_AUTO_CREATE,
-                        new UserHandle(userid)))
-                {
-                    mServicesBinding.remove(servicesBindingTag);
-                    Slog.w(TAG, "Unable to bind listener service: " + intent);
-                    return;
-                }
-            } catch (SecurityException ex) {
-                Slog.e(TAG, "Unable to bind listener service: " + intent, ex);
-                return;
-            }
-        }
-    }
-
-    /**
-     * Remove a listener service for the given user by ComponentName
-     */
-    private void unregisterListenerService(ComponentName name, int userid) {
-        NotificationUtil.checkCallerIsSystem();
-
-        synchronized (mMutex) {
-            final int N = mListeners.size();
-            for (int i=N-1; i>=0; i--) {
-                final NotificationListenerInfo info = mListeners.get(i);
-                if (name.equals(info.component)
-                        && info.userid == userid) {
-                    mListeners.remove(i);
-                    if (info.connection != null) {
-                        try {
-                            mContext.unbindService(info.connection);
-                        } catch (IllegalArgumentException ex) {
-                            // something happened to the service: we think we have a connection
-                            // but it's bogus.
-                            Slog.e(TAG, "Listener " + name + " could not be unbound: " + ex);
-                        }
-                    }
-                }
-            }
-        }
-    }
-
-    /**
-     * Removes a listener from the list but does not unbind from the listener's service.
-     *
-     * @return the removed listener.
-     */
-    private NotificationListenerInfo removeListenerImpl(
-            final INotificationListener listener, final int userid) {
-        NotificationListenerInfo listenerInfo = null;
-        synchronized (mMutex) {
-            final int N = mListeners.size();
-            for (int i=N-1; i>=0; i--) {
-                final NotificationListenerInfo info = mListeners.get(i);
-                if (info.listener.asBinder() == listener.asBinder()
-                        && info.userid == userid) {
-                    listenerInfo = mListeners.remove(i);
-                }
-            }
-        }
-        return listenerInfo;
-    }
-
-    private void checkNullListener(INotificationListener listener) {
-        if (listener == null) {
-            throw new IllegalArgumentException("Listener must not be null");
-        }
-    }
-
-    private void registerListenerImpl(final INotificationListener listener,
-            final ComponentName component, final int userid) {
-        synchronized (mMutex) {
-            try {
-                NotificationListenerInfo info
-                        = new NotificationListenerInfo(listener, component, userid,
-                        /*isSystem*/ true, Build.VERSION_CODES.L);
-                listener.asBinder().linkToDeath(info, 0);
-                mListeners.add(info);
-            } catch (RemoteException e) {
-                // already dead
-            }
-        }
-    }
-
-    /**
-     * Removes a listener from the list and unbinds from its service.
-     */
-    private void unregisterListenerImpl(final INotificationListener listener, final int userid) {
-        NotificationListenerInfo info = removeListenerImpl(listener, userid);
-        if (info != null && info.connection != null) {
-            mContext.unbindService(info.connection);
-        }
-    }
-
-    private class SettingsObserver extends ContentObserver {
-        private final Uri ENABLED_NOTIFICATION_LISTENERS_URI
-                = Settings.Secure.getUriFor(Settings.Secure.ENABLED_NOTIFICATION_LISTENERS);
-
-        private SettingsObserver(Handler handler) {
-            super(handler);
-        }
-
-        private void observe() {
-            ContentResolver resolver = mContext.getContentResolver();
-            resolver.registerContentObserver(ENABLED_NOTIFICATION_LISTENERS_URI,
-                    false, this, UserHandle.USER_ALL);
-            update(null);
-        }
-
-        @Override
-        public void onChange(boolean selfChange, Uri uri) {
-            update(uri);
-        }
-
-        private void update(Uri uri) {
-            if (uri == null || ENABLED_NOTIFICATION_LISTENERS_URI.equals(uri)) {
-                rebindListenerServices();
-            }
-        }
-    }
-
-    public class NotificationListenerInfo implements IBinder.DeathRecipient {
-        public INotificationListener listener;
-        public ComponentName component;
-        public int userid;
-        public boolean isSystem;
-        public ServiceConnection connection;
-        public int targetSdkVersion;
-
-        public NotificationListenerInfo(INotificationListener listener, ComponentName component,
-                int userid, boolean isSystem, int targetSdkVersion) {
-            this.listener = listener;
-            this.component = component;
-            this.userid = userid;
-            this.isSystem = isSystem;
-            this.connection = null;
-            this.targetSdkVersion = targetSdkVersion;
-        }
-
-        public NotificationListenerInfo(INotificationListener listener, ComponentName component,
-                int userid, ServiceConnection connection, int targetSdkVersion) {
-            this.listener = listener;
-            this.component = component;
-            this.userid = userid;
-            this.isSystem = false;
-            this.connection = connection;
-            this.targetSdkVersion = targetSdkVersion;
-        }
-
-        public boolean enabledAndUserMatches(StatusBarNotification sbn) {
-            final int nid = sbn.getUserId();
-            if (!isEnabledForCurrentProfiles()) {
-                return false;
-            }
-            if (this.userid == UserHandle.USER_ALL) return true;
-            if (nid == UserHandle.USER_ALL || nid == this.userid) return true;
-            return supportsProfiles() && mUserProfiles.isCurrentProfile(nid);
-        }
-
-        public boolean supportsProfiles() {
-            return targetSdkVersion >= Build.VERSION_CODES.L;
-        }
-
-        public void notifyPostedIfUserMatch(StatusBarNotification sbn) {
-            if (!enabledAndUserMatches(sbn)) {
-                return;
-            }
-            try {
-                listener.onNotificationPosted(sbn);
-            } catch (RemoteException ex) {
-                Log.e(TAG, "unable to notify listener (posted): " + listener, ex);
-            }
-        }
-
-        public void notifyRemovedIfUserMatch(StatusBarNotification sbn) {
-            if (!enabledAndUserMatches(sbn)) return;
-            try {
-                listener.onNotificationRemoved(sbn);
-            } catch (RemoteException ex) {
-                Log.e(TAG, "unable to notify listener (removed): " + listener, ex);
-            }
-        }
-
-        @Override
-        public void binderDied() {
-            // Remove the listener, but don't unbind from the service. The system will bring the
-            // service back up, and the onServiceConnected handler will readd the listener with the
-            // new binding. If this isn't a bound service, and is just a registered
-            // INotificationListener, just removing it from the list is all we need to do anyway.
-            removeListenerImpl(this.listener, this.userid);
-        }
-
-        /** convenience method for looking in mEnabledListenersForCurrentProfiles */
-        public boolean isEnabledForCurrentProfiles() {
-            if (this.isSystem) return true;
-            if (this.connection == null) return false;
-            return mEnabledListenersForCurrentProfiles.contains(this.component);
-        }
-    }
-}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 5f096cb..6e4eb56 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -22,6 +22,7 @@
 
 import android.app.ActivityManager;
 import android.app.ActivityManagerNative;
+import android.app.AppGlobals;
 import android.app.AppOpsManager;
 import android.app.IActivityManager;
 import android.app.INotificationManager;
@@ -35,10 +36,10 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.pm.UserInfo;
 import android.content.res.Resources;
 import android.database.ContentObserver;
 import android.graphics.Bitmap;
@@ -49,15 +50,19 @@
 import android.os.Environment;
 import android.os.Handler;
 import android.os.IBinder;
+import android.os.IInterface;
 import android.os.Message;
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.UserHandle;
-import android.os.UserManager;
 import android.os.Vibrator;
 import android.provider.Settings;
 import android.service.notification.INotificationListener;
+import android.service.notification.IConditionListener;
+import android.service.notification.IConditionProvider;
+import android.service.notification.NotificationListenerService;
 import android.service.notification.StatusBarNotification;
+import android.service.notification.Condition;
 import android.service.notification.ZenModeConfig;
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
@@ -65,7 +70,6 @@
 import android.util.AtomicFile;
 import android.util.Log;
 import android.util.Slog;
-import android.util.SparseArray;
 import android.util.Xml;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityManager;
@@ -78,7 +82,8 @@
 import com.android.server.SystemService;
 import com.android.server.lights.Light;
 import com.android.server.lights.LightsManager;
-import com.android.server.notification.NotificationListeners.NotificationListenerInfo;
+import com.android.server.notification.ManagedServices.ManagedServiceInfo;
+import com.android.server.notification.ManagedServices.UserProfiles;
 import com.android.server.notification.NotificationUsageStats.SingleNotificationStats;
 import com.android.server.statusbar.StatusBarManagerInternal;
 
@@ -101,7 +106,6 @@
 import java.util.Arrays;
 import java.util.HashSet;
 import java.util.Iterator;
-import java.util.List;
 import java.util.NoSuchElementException;
 
 /** {@hide} */
@@ -191,8 +195,9 @@
 
     final ArrayList<NotificationScorer> mScorers = new ArrayList<NotificationScorer>();
 
-    private NotificationListeners mListeners;
     private final UserProfiles mUserProfiles = new UserProfiles();
+    private NotificationListeners mListeners;
+    private ConditionProviders mConditionProviders;
 
     private final NotificationUsageStats mUsageStats = new NotificationUsageStats();
 
@@ -702,7 +707,7 @@
                 String pkgList[] = null;
                 boolean queryReplace = queryRemove &&
                         intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
-                if (DBG) Slog.i(TAG, "queryReplace=" + queryReplace);
+                if (DBG) Slog.i(TAG, "action=" + action + " queryReplace=" + queryReplace);
                 if (action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) {
                     pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
                 } else if (queryRestart) {
@@ -745,6 +750,7 @@
                     }
                 }
                 mListeners.onPackagesChanged(queryReplace, pkgList);
+                mConditionProviders.onPackagesChanged(queryReplace, pkgList);
             } else if (action.equals(Intent.ACTION_SCREEN_ON)) {
                 // Keep track of screen on/off state, but do not turn off the notification light
                 // until user passes through the lock screen or views the notification.
@@ -834,7 +840,7 @@
 
         mHandler = new WorkerHandler();
         mZenModeHelper = new ZenModeHelper(getContext(), mHandler);
-        mZenModeHelper.setCallback(new ZenModeHelper.Callback() {
+        mZenModeHelper.addCallback(new ZenModeHelper.Callback() {
             @Override
             public void onConfigChanged() {
                 savePolicyFile();
@@ -845,18 +851,9 @@
 
         importOldBlockDb();
 
-        mListeners = new NotificationListeners(getContext(),
-                mHandler, mNotificationList, mUserProfiles) {
-            @Override
-            public void onServiceAdded(INotificationListener listener) {
-                final String[] keys = getActiveNotificationKeysFromListener(listener);
-                try {
-                    listener.onListenerConnected(keys);
-                } catch (RemoteException e) {
-                    // we tried
-                }
-            }
-        };
+        mListeners = new NotificationListeners();
+        mConditionProviders = new ConditionProviders(getContext(),
+                mHandler, mUserProfiles, mZenModeHelper);
         mStatusBar = getLocalService(StatusBarManagerInternal.class);
         mStatusBar.setNotificationDelegate(mNotificationDelegate);
 
@@ -972,6 +969,7 @@
             // bind to listener services.
             mSettingsObserver.observe();
             mListeners.onBootPhaseAppsCanStart();
+            mConditionProviders.onBootPhaseAppsCanStart();
         }
     }
 
@@ -1005,8 +1003,7 @@
                 return ;
             }
 
-            final boolean isSystemToast =
-                    NotificationUtil.isCallerSystem() || ("android".equals(pkg));
+            final boolean isSystemToast = isCallerSystem() || ("android".equals(pkg));
 
             if (ENABLE_BLOCKED_TOASTS && !noteNotificationOp(pkg, Binder.getCallingUid())) {
                 if (!isSystemToast) {
@@ -1097,7 +1094,7 @@
 
         @Override
         public void cancelNotificationWithTag(String pkg, String tag, int id, int userId) {
-            NotificationUtil.checkCallerIsSystemOrSameApp(pkg);
+            checkCallerIsSystemOrSameApp(pkg);
             userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
                     Binder.getCallingUid(), userId, true, false, "cancelNotificationWithTag", pkg);
             // Don't allow client applications to cancel foreground service notis.
@@ -1109,7 +1106,7 @@
 
         @Override
         public void cancelAllNotifications(String pkg, int userId) {
-            NotificationUtil.checkCallerIsSystemOrSameApp(pkg);
+            checkCallerIsSystemOrSameApp(pkg);
 
             userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
                     Binder.getCallingUid(), userId, true, false, "cancelAllNotifications", pkg);
@@ -1123,7 +1120,7 @@
 
         @Override
         public void setNotificationsEnabledForPackage(String pkg, int uid, boolean enabled) {
-            NotificationUtil.checkCallerIsSystem();
+            checkCallerIsSystem();
 
             setNotificationsEnabledForPackageImpl(pkg, uid, enabled);
         }
@@ -1133,7 +1130,7 @@
          */
         @Override
         public boolean areNotificationsEnabledForPackage(String pkg, int uid) {
-            NotificationUtil.checkCallerIsSystem();
+            checkCallerIsSystem();
             return (mAppOps.checkOpNoThrow(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg)
                     == AppOpsManager.MODE_ALLOWED);
         }
@@ -1201,8 +1198,8 @@
         @Override
         public void registerListener(final INotificationListener listener,
                 final ComponentName component, final int userid) {
-            NotificationUtil.checkCallerIsSystem();
-            mListeners.registerListener(listener, component, userid);
+            checkCallerIsSystem();
+            mListeners.registerService(listener, component, userid);
         }
 
         /**
@@ -1210,7 +1207,7 @@
          */
         @Override
         public void unregisterListener(INotificationListener listener, int userid) {
-            mListeners.unregisterListener(listener, userid);
+            mListeners.unregisterService(listener, userid);
         }
 
         /**
@@ -1227,8 +1224,7 @@
             long identity = Binder.clearCallingIdentity();
             try {
                 synchronized (mNotificationList) {
-                    final NotificationListenerInfo info =
-                            mListeners.checkListenerTokenLocked(token);
+                    final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
                     if (keys != null) {
                         final int N = keys.length;
                         for (int i = 0; i < N; i++) {
@@ -1237,7 +1233,7 @@
                             if (userId != info.userid && userId != UserHandle.USER_ALL &&
                                     !mUserProfiles.isCurrentProfile(userId)) {
                                 throw new SecurityException("Disallowed call from listener: "
-                                        + info.listener);
+                                        + info.service);
                             }
                             if (r != null) {
                                 cancelNotificationFromListenerLocked(info, callingUid, callingPid,
@@ -1255,7 +1251,7 @@
             }
         }
 
-        private void cancelNotificationFromListenerLocked(NotificationListenerInfo info,
+        private void cancelNotificationFromListenerLocked(ManagedServiceInfo info,
                 int callingUid, int callingPid, String pkg, String tag, int id, int userId) {
             cancelNotification(callingUid, callingPid, pkg, tag, id, 0,
                     Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE,
@@ -1278,8 +1274,7 @@
             long identity = Binder.clearCallingIdentity();
             try {
                 synchronized (mNotificationList) {
-                    final NotificationListenerInfo info =
-                            mListeners.checkListenerTokenLocked(token);
+                    final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
                     if (info.supportsProfiles()) {
                         Log.e(TAG, "Ignoring deprecated cancelNotification(pkg, tag, id) "
                                 + "from " + info.component
@@ -1305,14 +1300,14 @@
         public StatusBarNotification[] getActiveNotificationsFromListener(
                 INotificationListener token, String[] keys) {
             synchronized (mNotificationList) {
-                final NotificationListenerInfo info = mListeners.checkListenerTokenLocked(token);
+                final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
                 final ArrayList<StatusBarNotification> list
                         = new ArrayList<StatusBarNotification>();
                 if (keys == null) {
                     final int N = mNotificationList.size();
                     for (int i=0; i<N; i++) {
                         StatusBarNotification sbn = mNotificationList.get(i).sbn;
-                        if (info.enabledAndUserMatches(sbn)) {
+                        if (info.enabledAndUserMatches(sbn.getUserId())) {
                             list.add(sbn);
                         }
                     }
@@ -1320,7 +1315,7 @@
                     final int N = keys.length;
                     for (int i=0; i<N; i++) {
                         NotificationRecord r = mNotificationsByKey.get(keys[i]);
-                        if (r != null && info.enabledAndUserMatches(r.sbn)) {
+                        if (r != null && info.enabledAndUserMatches(r.sbn.getUserId())) {
                             list.add(r.sbn);
                         }
                     }
@@ -1336,17 +1331,48 @@
 
         @Override
         public ZenModeConfig getZenModeConfig() {
-            NotificationUtil.checkCallerIsSystem();
+            checkCallerIsSystem();
             return mZenModeHelper.getConfig();
         }
 
         @Override
         public boolean setZenModeConfig(ZenModeConfig config) {
-            NotificationUtil.checkCallerIsSystem();
+            checkCallerIsSystem();
             return mZenModeHelper.setConfig(config);
         }
 
         @Override
+        public void notifyConditions(String pkg, IConditionProvider provider,
+                Condition[] conditions) {
+            final ManagedServiceInfo info = mConditionProviders.checkServiceToken(provider);
+            checkCallerIsSystemOrSameApp(pkg);
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                mConditionProviders.notifyConditions(pkg, info, conditions);
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+
+        @Override
+        public void requestZenModeConditions(IConditionListener callback, boolean requested) {
+            enforceSystemOrSystemUI("INotificationManager.requestZenModeConditions");
+            mConditionProviders.requestZenModeConditions(callback, requested);
+        }
+
+        @Override
+        public void setZenModeCondition(Uri conditionId) {
+            enforceSystemOrSystemUI("INotificationManager.setZenModeCondition");
+            mConditionProviders.setZenModeCondition(conditionId);
+        }
+
+        private void enforceSystemOrSystemUI(String message) {
+            if (isCallerSystem()) return;
+            getContext().enforceCallingPermission(android.Manifest.permission.STATUS_BAR_SERVICE,
+                    message);
+        }
+
+        @Override
         protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
             if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
                     != PackageManager.PERMISSION_GRANTED) {
@@ -1362,12 +1388,12 @@
 
     private String[] getActiveNotificationKeysFromListener(INotificationListener token) {
         synchronized (mNotificationList) {
-            final NotificationListenerInfo info = mListeners.checkListenerTokenLocked(token);
+            final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
             final ArrayList<String> keys = new ArrayList<String>();
             final int N = mNotificationList.size();
             for (int i=0; i<N; i++) {
                 final StatusBarNotification sbn = mNotificationList.get(i).sbn;
-                if (info.enabledAndUserMatches(sbn)) {
+                if (info.enabledAndUserMatches(sbn.getUserId())) {
                     keys.add(sbn.getKey());
                 }
             }
@@ -1378,8 +1404,6 @@
     void dumpImpl(PrintWriter pw) {
         pw.println("Current Notification Manager state:");
 
-        mListeners.dump(pw);
-
         int N;
 
         synchronized (mToastQueue) {
@@ -1433,6 +1457,12 @@
 
             pw.println("\n  Zen Mode:");
             mZenModeHelper.dump(pw, "    ");
+
+            pw.println("\n  Notification listeners:");
+            mListeners.dump(pw);
+
+            pw.println("\n  Condition providers:");
+            mConditionProviders.dump(pw);
         }
     }
 
@@ -1455,9 +1485,8 @@
             Slog.v(TAG, "enqueueNotificationInternal: pkg=" + pkg + " id=" + id
                     + " notification=" + notification);
         }
-        NotificationUtil.checkCallerIsSystemOrSameApp(pkg);
-        final boolean isSystemNotification =
-                NotificationUtil.isUidSystem(callingUid) || ("android".equals(pkg));
+        checkCallerIsSystemOrSameApp(pkg);
+        final boolean isSystemNotification = isUidSystem(callingUid) || ("android".equals(pkg));
 
         final int userId = ActivityManager.handleIncomingUser(callingPid,
                 callingUid, incomingUserId, true, false, "enqueueNotification", pkg);
@@ -2028,7 +2057,7 @@
     void cancelNotification(final int callingUid, final int callingPid,
             final String pkg, final String tag, final int id,
             final int mustHaveFlags, final int mustNotHaveFlags, final boolean sendDelete,
-            final int userId, final int reason, final NotificationListenerInfo listener) {
+            final int userId, final int reason, final ManagedServiceInfo listener) {
         // In enqueueNotificationInternal notifications are added by scheduling the
         // work on the worker handler. Hence, we also schedule the cancel on this
         // handler to avoid a scenario where an add notification call followed by a
@@ -2099,7 +2128,7 @@
      */
     boolean cancelAllNotificationsInt(int callingUid, int callingPid, String pkg, int mustHaveFlags,
             int mustNotHaveFlags, boolean doit, int userId, int reason,
-            NotificationListenerInfo listener) {
+            ManagedServiceInfo listener) {
         EventLogTags.writeNotificationCancelAll(callingUid, callingPid,
                 pkg, userId, mustHaveFlags, mustNotHaveFlags, reason,
                 listener == null ? null : listener.component.toShortString());
@@ -2141,7 +2170,7 @@
     }
 
     void cancelAllLocked(int callingUid, int callingPid, int userId, int reason,
-            NotificationListenerInfo listener, boolean includeCurrentProfiles) {
+            ManagedServiceInfo listener, boolean includeCurrentProfiles) {
         EventLogTags.writeNotificationCancelAll(callingUid, callingPid,
                 null, userId, 0, 0, reason,
                 listener == null ? null : listener.component.toShortString());
@@ -2234,38 +2263,129 @@
         }
     }
 
-    public static class UserProfiles {
-        // Profiles of the current user.
-        private final SparseArray<UserInfo> mCurrentProfiles = new SparseArray<UserInfo>();
+    private static boolean isUidSystem(int uid) {
+        final int appid = UserHandle.getAppId(uid);
+        return (appid == Process.SYSTEM_UID || appid == Process.PHONE_UID || uid == 0);
+    }
 
-        private void updateCache(Context context) {
-            UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
-            if (userManager != null) {
-                int currentUserId = ActivityManager.getCurrentUser();
-                List<UserInfo> profiles = userManager.getProfiles(currentUserId);
-                synchronized (mCurrentProfiles) {
-                    mCurrentProfiles.clear();
-                    for (UserInfo user : profiles) {
-                        mCurrentProfiles.put(user.id, user);
+    private static boolean isCallerSystem() {
+        return isUidSystem(Binder.getCallingUid());
+    }
+
+    private static void checkCallerIsSystem() {
+        if (isCallerSystem()) {
+            return;
+        }
+        throw new SecurityException("Disallowed call for uid " + Binder.getCallingUid());
+    }
+
+    private static void checkCallerIsSystemOrSameApp(String pkg) {
+        if (isCallerSystem()) {
+            return;
+        }
+        final int uid = Binder.getCallingUid();
+        try {
+            ApplicationInfo ai = AppGlobals.getPackageManager().getApplicationInfo(
+                    pkg, 0, UserHandle.getCallingUserId());
+            if (!UserHandle.isSameApp(ai.uid, uid)) {
+                throw new SecurityException("Calling uid " + uid + " gave package"
+                        + pkg + " which is owned by uid " + ai.uid);
+            }
+        } catch (RemoteException re) {
+            throw new SecurityException("Unknown package " + pkg + "\n" + re);
+        }
+    }
+
+    public class NotificationListeners extends ManagedServices {
+
+        public NotificationListeners() {
+            super(getContext(), mHandler, mNotificationList, mUserProfiles);
+        }
+
+        @Override
+        protected Config getConfig() {
+            Config c = new Config();
+            c.caption = "notification listener";
+            c.serviceInterface = NotificationListenerService.SERVICE_INTERFACE;
+            c.secureSettingName = Settings.Secure.ENABLED_NOTIFICATION_LISTENERS;
+            c.bindPermission = android.Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE;
+            c.settingsAction = Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS;
+            c.clientLabel = R.string.notification_listener_binding_label;
+            return c;
+        }
+
+        @Override
+        protected IInterface asInterface(IBinder binder) {
+            return INotificationListener.Stub.asInterface(binder);
+        }
+
+        @Override
+        public void onServiceAdded(IInterface service) {
+            final INotificationListener listener = (INotificationListener) service;
+            final String[] keys = getActiveNotificationKeysFromListener(listener);
+            try {
+                listener.onListenerConnected(keys);
+            } catch (RemoteException e) {
+                // we tried
+            }
+        }
+
+        /**
+         * asynchronously notify all listeners about a new notification
+         */
+        public void notifyPostedLocked(StatusBarNotification sbn) {
+            // make a copy in case changes are made to the underlying Notification object
+            final StatusBarNotification sbnClone = sbn.clone();
+            for (final ManagedServiceInfo info : mServices) {
+                mHandler.post(new Runnable() {
+                    @Override
+                    public void run() {
+                        notifyPostedIfUserMatch(info, sbnClone);
                     }
-                }
+                });
             }
         }
 
-        public int[] getCurrentProfileIds() {
-            synchronized (mCurrentProfiles) {
-                int[] users = new int[mCurrentProfiles.size()];
-                final int N = mCurrentProfiles.size();
-                for (int i = 0; i < N; ++i) {
-                    users[i] = mCurrentProfiles.keyAt(i);
-                }
-                return users;
+        /**
+         * asynchronously notify all listeners about a removed notification
+         */
+        public void notifyRemovedLocked(StatusBarNotification sbn) {
+            // make a copy in case changes are made to the underlying Notification object
+            // NOTE: this copy is lightweight: it doesn't include heavyweight parts of the
+            // notification
+            final StatusBarNotification sbnLight = sbn.cloneLight();
+            for (ManagedServiceInfo serviceInfo : mServices) {
+                final ManagedServiceInfo info = (ManagedServiceInfo) serviceInfo;
+                mHandler.post(new Runnable() {
+                    @Override
+                    public void run() {
+                        notifyRemovedIfUserMatch(info, sbnLight);
+                    }
+                });
             }
         }
 
-        public boolean isCurrentProfile(int userId) {
-            synchronized (mCurrentProfiles) {
-                return mCurrentProfiles.get(userId) != null;
+        private void notifyPostedIfUserMatch(ManagedServiceInfo info, StatusBarNotification sbn) {
+            if (!info.enabledAndUserMatches(sbn.getUserId())) {
+                return;
+            }
+            final INotificationListener listener = (INotificationListener)info.service;
+            try {
+                listener.onNotificationPosted(sbn);
+            } catch (RemoteException ex) {
+                Log.e(TAG, "unable to notify listener (posted): " + listener, ex);
+            }
+        }
+
+        private void notifyRemovedIfUserMatch(ManagedServiceInfo info, StatusBarNotification sbn) {
+            if (!info.enabledAndUserMatches(sbn.getUserId())) {
+                return;
+            }
+            final INotificationListener listener = (INotificationListener)info.service;
+            try {
+                listener.onNotificationRemoved(sbn);
+            } catch (RemoteException ex) {
+                Log.e(TAG, "unable to notify listener (removed): " + listener, ex);
             }
         }
     }
diff --git a/services/core/java/com/android/server/notification/NotificationUtil.java b/services/core/java/com/android/server/notification/NotificationUtil.java
deleted file mode 100644
index 459adce..0000000
--- a/services/core/java/com/android/server/notification/NotificationUtil.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/**
- * Copyright (c) 2014, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.notification;
-
-import android.app.AppGlobals;
-import android.content.pm.ApplicationInfo;
-import android.os.Binder;
-import android.os.Process;
-import android.os.RemoteException;
-import android.os.UserHandle;
-
-public class NotificationUtil {
-
-    // Return true if the UID is a system or phone UID and therefore should not have
-    // any notifications or toasts blocked.
-    public static boolean isUidSystem(int uid) {
-        final int appid = UserHandle.getAppId(uid);
-        return (appid == Process.SYSTEM_UID || appid == Process.PHONE_UID || uid == 0);
-    }
-
-    // same as isUidSystem(int, int) for the Binder caller's UID.
-    public static boolean isCallerSystem() {
-        return isUidSystem(Binder.getCallingUid());
-    }
-
-    public static void checkCallerIsSystem() {
-        if (isCallerSystem()) {
-            return;
-        }
-        throw new SecurityException("Disallowed call for uid " + Binder.getCallingUid());
-    }
-
-    public static void checkCallerIsSystemOrSameApp(String pkg) {
-        if (isCallerSystem()) {
-            return;
-        }
-        final int uid = Binder.getCallingUid();
-        try {
-            ApplicationInfo ai = AppGlobals.getPackageManager().getApplicationInfo(
-                    pkg, 0, UserHandle.getCallingUserId());
-            if (!UserHandle.isSameApp(ai.uid, uid)) {
-                throw new SecurityException("Calling uid " + uid + " gave package"
-                        + pkg + " which is owned by uid " + ai.uid);
-            }
-        } catch (RemoteException re) {
-            throw new SecurityException("Unknown package " + pkg + "\n" + re);
-        }
-    }
-}
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 80f5b5c..137730a 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -46,6 +46,7 @@
 
 import java.io.IOException;
 import java.io.PrintWriter;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Calendar;
 import java.util.Date;
@@ -69,8 +70,8 @@
     private final SettingsObserver mSettingsObserver;
     private final AppOpsManager mAppOps;
     private final ZenModeConfig mDefaultConfig;
+    private final ArrayList<Callback> mCallbacks = new ArrayList<Callback>();
 
-    private Callback mCallback;
     private int mZenMode;
     private ZenModeConfig mConfig;
 
@@ -115,8 +116,8 @@
         return new ZenModeConfig();
     }
 
-    public void setCallback(Callback callback) {
-        mCallback = callback;
+    public void addCallback(Callback callback) {
+        mCallbacks.add(callback);
     }
 
     public boolean shouldIntercept(String pkg, Notification n) {
@@ -132,6 +133,14 @@
         return false;
     }
 
+    public int getZenMode() {
+        return mZenMode;
+    }
+
+    public void setZenMode(int zenModeValue) {
+        Global.putInt(mContext.getContentResolver(), Global.ZEN_MODE, zenModeValue);
+    }
+
     public void updateZenMode() {
         final int mode = Global.getInt(mContext.getContentResolver(),
                 Global.ZEN_MODE, Global.ZEN_MODE_OFF);
@@ -157,6 +166,7 @@
         mAppOps.setRestriction(AppOpsManager.OP_VIBRATE, AudioManager.USE_DEFAULT_STREAM_TYPE,
                 zen ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED,
                 exceptionPackages);
+        dispatchOnZenModeChanged();
     }
 
     public boolean allowDisable(int what, IBinder token, String pkg) {
@@ -193,7 +203,7 @@
         if (config.equals(mConfig)) return true;
         mConfig = config;
         Slog.d(TAG, "mConfig=" + mConfig);
-        if (mCallback != null) mCallback.onConfigChanged();
+        dispatchOnConfigChanged();
         final String val = Integer.toString(mConfig.hashCode());
         Global.putString(mContext.getContentResolver(), Global.ZEN_MODE_CONFIG_ETAG, val);
         updateAlarms();
@@ -201,6 +211,18 @@
         return true;
     }
 
+    private void dispatchOnConfigChanged() {
+        for (Callback callback : mCallbacks) {
+            callback.onConfigChanged();
+        }
+    }
+
+    private void dispatchOnZenModeChanged() {
+        for (Callback callback : mCallbacks) {
+            callback.onZenModeChanged();
+        }
+    }
+
     private boolean isCall(String pkg, Notification n) {
         return CALL_PACKAGES.contains(pkg);
     }
@@ -300,13 +322,14 @@
             if (skip) {
                 Slog.d(TAG, "Skipping zen mode update for the weekend");
             } else {
-                Global.putInt(mContext.getContentResolver(), Global.ZEN_MODE, zenModeValue);
+                ZenModeHelper.this.setZenMode(zenModeValue);
             }
             updateAlarms();
         }
     }
 
-    public interface Callback {
-        void onConfigChanged();
+    public static class Callback {
+        void onConfigChanged() {}
+        void onZenModeChanged() {}
     }
 }
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 7683205..2c623b5 100755
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -1532,6 +1532,9 @@
                 mSettings.readDefaultPreferredAppsLPw(this, 0);
             }
 
+            // All the changes are done during package scanning.
+            mSettings.updateInternalDatabaseVersion();
+
             // can downgrade to reader
             mSettings.writeLPr();
 
@@ -2160,6 +2163,24 @@
     }
 
     @Override
+    public boolean activitySupportsIntent(ComponentName component, Intent intent,
+            String resolvedType) {
+        synchronized (mPackages) {
+            PackageParser.Activity a = mActivities.mActivities.get(component);
+            if (a == null) {
+                return false;
+            }
+            for (int i=0; i<a.intents.size(); i++) {
+                if (a.intents.get(i).match(intent.getAction(), resolvedType, intent.getScheme(),
+                        intent.getData(), intent.getCategories(), TAG) >= 0) {
+                    return true;
+                }
+            }
+            return false;
+        }
+    }
+
+    @Override
     public ActivityInfo getReceiverInfo(ComponentName component, int flags, int userId) {
         if (!sUserManager.exists(userId)) return null;
         enforceCrossUserPermission(Binder.getCallingUid(), userId, false, "get receiver info");
@@ -5418,7 +5439,7 @@
         if (FileUtils.contains(Environment.getRootDirectory(), codePath)) {
             codeRoot = Environment.getRootDirectory();
         } else if (FileUtils.contains(Environment.getOemDirectory(), codePath)) {
-            codeRoot = Environment.getRootDirectory();
+            codeRoot = Environment.getOemDirectory();
         } else if (FileUtils.contains(Environment.getVendorDirectory(), codePath)) {
             codeRoot = Environment.getVendorDirectory();
         } else {
@@ -11056,6 +11077,8 @@
 
         public static final int DUMP_KEYSETS = 1 << 11;
 
+        public static final int DUMP_VERSION = 1 << 12;
+
         public static final int OPTION_SHOW_FILTERS = 1 << 0;
 
         private int mTypes;
@@ -11154,6 +11177,7 @@
                 pw.println("    s[hared-users]: dump shared user IDs");
                 pw.println("    m[essages]: print collected runtime messages");
                 pw.println("    v[erifiers]: print package verifier info");
+                pw.println("    version: print database version info");
                 pw.println("    <package.name>: info about given package");
                 pw.println("    k[eysets]: print known keysets");
                 return;
@@ -11202,6 +11226,8 @@
                 dumpState.setDump(DumpState.DUMP_MESSAGES);
             } else if ("v".equals(cmd) || "verifiers".equals(cmd)) {
                 dumpState.setDump(DumpState.DUMP_VERIFIERS);
+            } else if ("version".equals(cmd)) {
+                dumpState.setDump(DumpState.DUMP_VERSION);
             } else if ("k".equals(cmd) || "keysets".equals(cmd)) {
                 dumpState.setDump(DumpState.DUMP_KEYSETS);
             }
@@ -11213,6 +11239,24 @@
 
         // reader
         synchronized (mPackages) {
+            if (dumpState.isDumping(DumpState.DUMP_VERSION) && packageName == null) {
+                if (!checkin) {
+                    if (dumpState.onTitlePrinted())
+                        pw.println();
+                    pw.println("Database versions:");
+                    pw.print("  SDK Version:");
+                    pw.print(" internal=");
+                    pw.print(mSettings.mInternalSdkPlatform);
+                    pw.print(" external=");
+                    pw.println(mSettings.mExternalSdkPlatform);
+                    pw.print("  DB Version:");
+                    pw.print(" internal=");
+                    pw.print(mSettings.mInternalDatabaseVersion);
+                    pw.print(" external=");
+                    pw.println(mSettings.mExternalDatabaseVersion);
+                }
+            }
+
             if (dumpState.isDumping(DumpState.DUMP_VERIFIERS) && packageName == null) {
                 if (!checkin) {
                     if (dumpState.onTitlePrinted())
@@ -11743,6 +11787,9 @@
                     | (regrantPermissions
                             ? (UPDATE_PERMISSIONS_REPLACE_PKG|UPDATE_PERMISSIONS_REPLACE_ALL)
                             : 0));
+
+            mSettings.updateExternalDatabaseVersion();
+
             // can downgrade to reader
             // Persist settings
             mSettings.writeLPr();
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 80f716c..a69940f 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -89,6 +89,28 @@
 final class Settings {
     private static final String TAG = "PackageSettings";
 
+    /**
+     * Current version of the package database. Set it to the latest version in
+     * the {@link DatabaseVersion} class below to ensure the database upgrade
+     * doesn't happen repeatedly.
+     * <p>
+     * Note that care should be taken to make sure all database upgrades are
+     * idempotent.
+     */
+    private static final int CURRENT_DATABASE_VERSION = DatabaseVersion.FIRST_VERSION;
+
+    /**
+     * This class contains constants that can be referred to from upgrade code.
+     * Insert constant values here that describe the upgrade reason. The version
+     * code must be monotonically increasing.
+     */
+    public static class DatabaseVersion {
+        /**
+         * The initial version of the database.
+         */
+        public static final int FIRST_VERSION = 1;
+    }
+
     private static final boolean DEBUG_STOPPED = false;
     private static final boolean DEBUG_MU = false;
 
@@ -133,6 +155,14 @@
     int mInternalSdkPlatform;
     int mExternalSdkPlatform;
 
+    /**
+     * The current database version for apps on internal storage. This is
+     * used to upgrade the format of the packages.xml database not necessarily
+     * tied to an SDK version.
+     */
+    int mInternalDatabaseVersion;
+    int mExternalDatabaseVersion;
+
     Boolean mReadExternalStorageEnforced;
 
     /** Device identity for the purpose of package verification. */
@@ -825,6 +855,40 @@
         }
     }
 
+    /**
+     * Returns whether the current database has is older than {@code version}
+     * for apps on internal storage.
+     */
+    public boolean isInternalDatabaseVersionOlderThan(int version) {
+        return mInternalDatabaseVersion < version;
+    }
+
+    /**
+     * Returns whether the current database has is older than {@code version}
+     * for apps on external storage.
+     */
+    public boolean isExternalDatabaseVersionOlderThan(int version) {
+        return mExternalDatabaseVersion < version;
+    }
+
+    /**
+     * Updates the database version for apps on internal storage. Called after
+     * call the updates to the database format are done for apps on internal
+     * storage after the initial start-up scan.
+     */
+    public void updateInternalDatabaseVersion() {
+        mInternalDatabaseVersion = CURRENT_DATABASE_VERSION;
+    }
+
+    /**
+     * Updates the database version for apps on internal storage. Called after
+     * call the updates to the database format are done for apps on internal
+     * storage after the initial start-up scan.
+     */
+    public void updateExternalDatabaseVersion() {
+        mExternalDatabaseVersion = CURRENT_DATABASE_VERSION;
+    }
+
     private void readPreferredActivitiesLPw(XmlPullParser parser, int userId)
             throws XmlPullParserException, IOException {
         int outerDepth = parser.getDepth();
@@ -1355,6 +1419,11 @@
             serializer.attribute(null, "external", Integer.toString(mExternalSdkPlatform));
             serializer.endTag(null, "last-platform-version");
 
+            serializer.startTag(null, "database-version");
+            serializer.attribute(null, "internal", Integer.toString(mInternalDatabaseVersion));
+            serializer.attribute(null, "external", Integer.toString(mExternalDatabaseVersion));
+            serializer.endTag(null, "database-version");
+
             if (mVerifierDeviceIdentity != null) {
                 serializer.startTag(null, "verifier");
                 serializer.attribute(null, "device", mVerifierDeviceIdentity.toString());
@@ -1830,6 +1899,19 @@
                         }
                     } catch (NumberFormatException e) {
                     }
+                } else if (tagName.equals("database-version")) {
+                    mInternalDatabaseVersion = mExternalDatabaseVersion = 0;
+                    try {
+                        String internalDbVersionString = parser.getAttributeValue(null, "internal");
+                        if (internalDbVersionString != null) {
+                            mInternalDatabaseVersion = Integer.parseInt(internalDbVersionString);
+                        }
+                        String externalDbVersionString = parser.getAttributeValue(null, "external");
+                        if (externalDbVersionString != null) {
+                            mExternalDatabaseVersion = Integer.parseInt(externalDbVersionString);
+                        }
+                    } catch (NumberFormatException ignored) {
+                    }
                 } else if (tagName.equals("verifier")) {
                     final String deviceIdentity = parser.getAttributeValue(null, "device");
                     try {
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 210e151b..f8103de 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -22,7 +22,6 @@
 import android.app.ActivityManager;
 import android.app.ActivityManagerNative;
 import android.app.ActivityThread;
-import android.app.admin.DevicePolicyManager;
 import android.app.IStopUserCallback;
 import android.app.admin.DevicePolicyManager;
 import android.content.BroadcastReceiver;
@@ -263,46 +262,58 @@
         if (userId != UserHandle.getCallingUserId()) {
             checkManageUsersPermission("getting profiles related to user " + userId);
         }
-        synchronized (mPackagesLock) {
-            // Getting the service here is not good for testing purposes. However, this service
-            // is not available when UserManagerService starts up so we need a lazy load.
-
-            DevicePolicyManager dpm = null;
-            if (enabledOnly) {
-                dpm = (DevicePolicyManager)
-                        mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            synchronized (mPackagesLock) {
+                return getProfilesLocked(userId, enabledOnly);
             }
-
-            UserInfo user = getUserInfoLocked(userId);
-            ArrayList<UserInfo> users = new ArrayList<UserInfo>(mUsers.size());
-            for (int i = 0; i < mUsers.size(); i++) {
-                UserInfo profile = mUsers.valueAt(i);
-                if (!isProfileOf(user, profile)) {
-                    continue;
-                }
-
-                if (enabledOnly && profile.isManagedProfile()) {
-                    if (dpm != null) {
-                        if(!dpm.isProfileEnabled(profile.id)) {
-                            continue;
-                        }
-                    } else {
-                        Log.w(LOG_TAG,
-                                "Attempting to reach DevicePolicyManager before it was started");
-                        // TODO: There might be system apps that need to call this. Make sure that
-                        // DevicePolicyManagerService is ready at that time (otherwise, any default
-                        // value is a bad one).
-                        throw new IllegalArgumentException(String.format(
-                                "Attempting to get enabled profiles for %d before "
-                                + "DevicePolicyManagerService has been started.", userId));
-                    }
-                }
-                users.add(profile);
-            }
-            return users;
+        } finally {
+            Binder.restoreCallingIdentity(ident);
         }
     }
 
+    /** Assume permissions already checked and caller's identity cleared */
+    private List<UserInfo> getProfilesLocked(int userId, boolean enabledOnly) {
+        // Getting the service here is not good for testing purposes.
+        // However, this service is not available when UserManagerService starts
+        // up so we need a lazy load.
+
+        DevicePolicyManager dpm = null;
+        if (enabledOnly) {
+            dpm = (DevicePolicyManager)
+                    mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
+        }
+
+        UserInfo user = getUserInfoLocked(userId);
+        ArrayList<UserInfo> users = new ArrayList<UserInfo>(mUsers.size());
+        for (int i = 0; i < mUsers.size(); i++) {
+            UserInfo profile = mUsers.valueAt(i);
+            if (!isProfileOf(user, profile)) {
+                continue;
+            }
+
+            if (enabledOnly && profile.isManagedProfile()) {
+                if (dpm != null) {
+                    if (!dpm.isProfileEnabled(profile.id)) {
+                        continue;
+                    }
+                } else {
+                    Log.w(LOG_TAG,
+                            "Attempting to reach DevicePolicyManager before it is started");
+                    // TODO: There might be system apps that need to call this.
+                    // Make sure that DevicePolicyManagerService is ready at that
+                    // time (otherwise, any default value is a bad one).
+                    throw new IllegalArgumentException(String.format(
+                            "Attempting to get enabled profiles for %d before "
+                                    + "DevicePolicyManagerService has been started.",
+                            userId));
+                }
+            }
+            users.add(profile);
+        }
+        return users;
+    }
+
     private boolean isProfileOf(UserInfo user, UserInfo profile) {
         return user.id == profile.id ||
                 (user.profileGroupId != UserInfo.NO_PROFILE_GROUP_ID
@@ -459,13 +470,13 @@
 
         synchronized (mPackagesLock) {
             Bundle restrictions = mUserRestrictions.get(userId);
-            return restrictions != null ? restrictions : Bundle.EMPTY;
+            return restrictions != null ? new Bundle(restrictions) : new Bundle();
         }
     }
 
     @Override
     public void setUserRestrictions(Bundle restrictions, int userId) {
-        checkProfileOwnerOrManageUsersPermission("setUserRestrictions");
+        checkManageUsersPermission("setUserRestrictions");
         if (restrictions == null) return;
 
         synchronized (mPackagesLock) {
@@ -491,51 +502,14 @@
      * @param message used as message if SecurityException is thrown
      * @throws SecurityException if the caller is not system or root
      */
-    private final void checkManageUsersPermission(String message) {
+    private static final void checkManageUsersPermission(String message) {
         final int uid = Binder.getCallingUid();
-
-        if (missingManageUsersPermission(uid)) {
-            throw new SecurityException("You need MANAGE_USERS permission to: " + message);
-        }
-    }
-
-    /**
-     * Enforces that only the system UID, root's UID, apps that have the
-     * {@link android.Manifest.permission#MANAGE_USERS MANAGE_USERS}
-     * permission, the profile owner, or the device owner can make certain calls to the
-     * UserManager.
-     *
-     * @param message used as message if SecurityException is thrown
-     * @throws SecurityException if the caller is not system, root, or device
-     * owner
-     */
-    private final void checkProfileOwnerOrManageUsersPermission(String message) {
-        final int uid = Binder.getCallingUid();
-        boolean isProfileOwner = false;
-        if (mContext != null && mContext.getPackageManager() != null) {
-            String[] pkgs = mContext.getPackageManager().getPackagesForUid(uid);
-            DevicePolicyManager dpm =
-                    (DevicePolicyManager) mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
-            if (dpm != null) {
-                for (String pkg : pkgs) {
-                    if (dpm.isDeviceOwnerApp(pkg) || dpm.isProfileOwnerApp(pkg)) {
-                        isProfileOwner = true;
-                    }
-                }
-            }
-        }
-
-        if (missingManageUsersPermission(uid) && !isProfileOwner) {
-            throw new SecurityException(
-                    "You need MANAGE_USERS permission or device owner privileges to: " + message);
-        }
-    }
-
-    private boolean missingManageUsersPermission(int uid) {
-        return uid != Process.SYSTEM_UID && uid != 0
+        if (uid != Process.SYSTEM_UID && uid != 0
                 && ActivityManager.checkComponentPermission(
                         android.Manifest.permission.MANAGE_USERS,
-                        uid, -1, true) != PackageManager.PERMISSION_GRANTED;
+                        uid, -1, true) != PackageManager.PERMISSION_GRANTED) {
+            throw new SecurityException("You need MANAGE_USERS permission to: " + message);
+        }
     }
 
     private void writeBitmapLocked(UserInfo info, Bitmap bitmap) {
@@ -1240,8 +1214,7 @@
     public Bundle getApplicationRestrictionsForUser(String packageName, int userId) {
         if (UserHandle.getCallingUserId() != userId
                 || !UserHandle.isSameApp(Binder.getCallingUid(), getUidForPackage(packageName))) {
-            checkProfileOwnerOrManageUsersPermission(
-                    "Only system or device owner can get restrictions for other users/apps");
+            checkManageUsersPermission("Only system can get restrictions for other users/apps");
         }
         synchronized (mPackagesLock) {
             // Read the restrictions from XML
@@ -1254,8 +1227,7 @@
             int userId) {
         if (UserHandle.getCallingUserId() != userId
                 || !UserHandle.isSameApp(Binder.getCallingUid(), getUidForPackage(packageName))) {
-            checkProfileOwnerOrManageUsersPermission(
-                    "Only system or device owner can set restrictions for other users/apps");
+            checkManageUsersPermission("Only system can set restrictions for other users/apps");
         }
         synchronized (mPackagesLock) {
             // Write the restrictions to XML
@@ -1357,8 +1329,7 @@
 
     @Override
     public void removeRestrictions() {
-        checkProfileOwnerOrManageUsersPermission(
-                "Only system or device owner can remove restrictions");
+        checkManageUsersPermission("Only system can remove restrictions");
         final int userHandle = UserHandle.getCallingUserId();
         removeRestrictionsForUser(userHandle, true);
     }
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index 6700895..5ceb992 100644
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -19,6 +19,9 @@
 import android.app.ActivityManager;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
+import android.content.ContentResolver;
+import android.content.ContentUris;
+import android.content.ContentValues;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
@@ -26,13 +29,18 @@
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
+import android.database.Cursor;
 import android.graphics.Rect;
 import android.net.Uri;
 import android.os.Binder;
+import android.os.Handler;
 import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.UserHandle;
+import android.provider.TvContract;
 import android.tv.ITvInputClient;
 import android.tv.ITvInputManager;
 import android.tv.ITvInputService;
@@ -41,13 +49,14 @@
 import android.tv.ITvInputSessionCallback;
 import android.tv.TvInputInfo;
 import android.tv.TvInputService;
-import android.util.ArrayMap;
 import android.util.Log;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.view.Surface;
 
 import com.android.internal.content.PackageMonitor;
+import com.android.internal.os.SomeArgs;
+import com.android.server.IoThread;
 import com.android.server.SystemService;
 
 import java.util.ArrayList;
@@ -63,6 +72,8 @@
 
     private final Context mContext;
 
+    private final ContentResolver mContentResolver;
+
     // A global lock.
     private final Object mLock = new Object();
 
@@ -72,10 +83,17 @@
     // A map from user id to UserState.
     private final SparseArray<UserState> mUserStates = new SparseArray<UserState>();
 
+    private final Handler mLogHandler;
+
     public TvInputManagerService(Context context) {
         super(context);
+
         mContext = context;
+        mContentResolver = context.getContentResolver();
+        mLogHandler = new LogHandler(IoThread.get().getLooper());
+
         registerBroadcastReceivers();
+
         synchronized (mLock) {
             mUserStates.put(mCurrentUserId, new UserState());
             buildTvInputListLocked(mCurrentUserId);
@@ -325,6 +343,14 @@
         UserState userState = getUserStateLocked(userId);
         SessionState sessionState = userState.sessionStateMap.remove(sessionToken);
 
+        // Close the open log entry, if any.
+        if (sessionState.logUri != null) {
+            SomeArgs args = SomeArgs.obtain();
+            args.arg1 = sessionState.logUri;
+            args.arg2 = System.currentTimeMillis();
+            mLogHandler.obtainMessage(LogHandler.MSG_CLOSE_ENTRY, args).sendToTarget();
+        }
+
         // Also remove the session token from the session token list of the current service.
         ServiceState serviceState = userState.serviceStateMap.get(sessionState.name);
         if (serviceState != null) {
@@ -384,7 +410,7 @@
                     UserState userState = getUserStateLocked(resolvedUserId);
                     ServiceState serviceState = userState.serviceStateMap.get(name);
                     if (serviceState == null) {
-                        serviceState = new ServiceState(name, resolvedUserId);
+                        serviceState = new ServiceState(resolvedUserId);
                         userState.serviceStateMap.put(name, serviceState);
                     }
                     IBinder iBinder = client.asBinder();
@@ -468,7 +494,7 @@
                     // Also, add them to the session state map of the current service.
                     ServiceState serviceState = userState.serviceStateMap.get(name);
                     if (serviceState == null) {
-                        serviceState = new ServiceState(name, resolvedUserId);
+                        serviceState = new ServiceState(resolvedUserId);
                         userState.serviceStateMap.put(name, serviceState);
                     }
                     serviceState.sessionTokens.add(sessionToken);
@@ -557,6 +583,35 @@
                 synchronized (mLock) {
                     try {
                         getSessionLocked(sessionToken, callingUid, resolvedUserId).tune(channelUri);
+
+                        long currentTime = System.currentTimeMillis();
+                        long channelId = ContentUris.parseId(channelUri);
+
+                        // Close the open log entry first, if any.
+                        UserState userState = getUserStateLocked(resolvedUserId);
+                        SessionState sessionState = userState.sessionStateMap.get(sessionToken);
+                        if (sessionState.logUri != null) {
+                            SomeArgs args = SomeArgs.obtain();
+                            args.arg1 = sessionState.logUri;
+                            args.arg2 = currentTime;
+                            mLogHandler.obtainMessage(LogHandler.MSG_CLOSE_ENTRY, args)
+                                    .sendToTarget();
+                        }
+
+                        // Create a log entry and fill it later.
+                        ContentValues values = new ContentValues();
+                        values.put(TvContract.WatchedPrograms.WATCH_START_TIME_UTC_MILLIS,
+                                currentTime);
+                        values.put(TvContract.WatchedPrograms.WATCH_END_TIME_UTC_MILLIS, 0);
+                        values.put(TvContract.WatchedPrograms.CHANNEL_ID, channelId);
+
+                        sessionState.logUri = mContentResolver.insert(
+                                TvContract.WatchedPrograms.CONTENT_URI, values);
+                        SomeArgs args = SomeArgs.obtain();
+                        args.arg1 = sessionState.logUri;
+                        args.arg2 = ContentUris.parseId(channelUri);
+                        args.arg3 = currentTime;
+                        mLogHandler.obtainMessage(LogHandler.MSG_OPEN_ENTRY, args).sendToTarget();
                     } catch (RemoteException e) {
                         Slog.e(TAG, "error in tune", e);
                         return;
@@ -652,7 +707,7 @@
         private boolean bound;
         private boolean available;
 
-        private ServiceState(ComponentName name, int userId) {
+        private ServiceState(int userId) {
             this.connection = new InputServiceConnection(userId);
         }
     }
@@ -664,6 +719,7 @@
         private final int callingUid;
 
         private ITvInputSession session;
+        private Uri logUri;
 
         private SessionState(ComponentName name, ITvInputClient client, int seq, int callingUid) {
             this.name = name;
@@ -738,4 +794,147 @@
             }
         }
     }
+
+    private final class LogHandler extends Handler {
+        private static final int MSG_OPEN_ENTRY = 1;
+        private static final int MSG_UPDATE_ENTRY = 2;
+        private static final int MSG_CLOSE_ENTRY = 3;
+
+        public LogHandler(Looper looper) {
+            super(looper);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case MSG_OPEN_ENTRY: {
+                    SomeArgs args = (SomeArgs) msg.obj;
+                    Uri uri = (Uri) args.arg1;
+                    long channelId = (long) args.arg2;
+                    long time = (long) args.arg3;
+                    onOpenEntry(uri, channelId, time);
+                    args.recycle();
+                    return;
+                }
+                case MSG_UPDATE_ENTRY: {
+                    SomeArgs args = (SomeArgs) msg.obj;
+                    Uri uri = (Uri) args.arg1;
+                    long channelId = (long) args.arg2;
+                    long time = (long) args.arg3;
+                    onUpdateEntry(uri, channelId, time);
+                    args.recycle();
+                    return;
+                }
+                case MSG_CLOSE_ENTRY: {
+                    SomeArgs args = (SomeArgs) msg.obj;
+                    Uri uri = (Uri) args.arg1;
+                    long time = (long) args.arg2;
+                    onCloseEntry(uri, time);
+                    args.recycle();
+                    return;
+                }
+                default: {
+                    Log.w(TAG, "Unhandled message code: " + msg.what);
+                    return;
+                }
+            }
+        }
+
+        private void onOpenEntry(Uri uri, long channelId, long watchStarttime) {
+            String[] projection = {
+                    TvContract.Programs.TITLE,
+                    TvContract.Programs.START_TIME_UTC_MILLIS,
+                    TvContract.Programs.END_TIME_UTC_MILLIS,
+                    TvContract.Programs.DESCRIPTION
+            };
+            String selection = TvContract.Programs.CHANNEL_ID + "=? AND "
+                    + TvContract.Programs.START_TIME_UTC_MILLIS + "<=? AND "
+                    + TvContract.Programs.END_TIME_UTC_MILLIS + ">?";
+            String[] selectionArgs = {
+                    String.valueOf(channelId),
+                    String.valueOf(watchStarttime),
+                    String.valueOf(watchStarttime)
+            };
+            String sortOrder = TvContract.Programs.START_TIME_UTC_MILLIS + " ASC";
+            Cursor cursor = null;
+            try {
+                cursor = mContentResolver.query(TvContract.Programs.CONTENT_URI, projection,
+                        selection, selectionArgs, sortOrder);
+                if (cursor != null && cursor.moveToNext()) {
+                    ContentValues values = new ContentValues();
+                    values.put(TvContract.WatchedPrograms.TITLE, cursor.getString(0));
+                    values.put(TvContract.WatchedPrograms.START_TIME_UTC_MILLIS, cursor.getLong(1));
+                    long endTime = cursor.getLong(2);
+                    values.put(TvContract.WatchedPrograms.END_TIME_UTC_MILLIS, endTime);
+                    values.put(TvContract.WatchedPrograms.DESCRIPTION, cursor.getString(3));
+                    mContentResolver.update(uri, values, null, null);
+
+                    // Schedule an update when the current program ends.
+                    SomeArgs args = SomeArgs.obtain();
+                    args.arg1 = uri;
+                    args.arg2 = channelId;
+                    args.arg3 = endTime;
+                    Message msg = obtainMessage(LogHandler.MSG_UPDATE_ENTRY, args);
+                    sendMessageDelayed(msg, endTime - System.currentTimeMillis());
+                }
+            } finally {
+                if (cursor != null) {
+                    cursor.close();
+                }
+            }
+        }
+
+        private void onUpdateEntry(Uri uri, long channelId, long time) {
+            String[] projection = {
+                    TvContract.WatchedPrograms.WATCH_START_TIME_UTC_MILLIS,
+                    TvContract.WatchedPrograms.WATCH_END_TIME_UTC_MILLIS,
+                    TvContract.WatchedPrograms.TITLE,
+                    TvContract.WatchedPrograms.START_TIME_UTC_MILLIS,
+                    TvContract.WatchedPrograms.END_TIME_UTC_MILLIS,
+                    TvContract.WatchedPrograms.DESCRIPTION
+            };
+            Cursor cursor = null;
+            try {
+                cursor = mContentResolver.query(uri, projection, null, null, null);
+                if (cursor != null && cursor.moveToNext()) {
+                    long watchStartTime = cursor.getLong(0);
+                    long watchEndTime = cursor.getLong(1);
+                    String title = cursor.getString(2);
+                    long startTime = cursor.getLong(3);
+                    long endTime = cursor.getLong(4);
+                    String description = cursor.getString(5);
+
+                    // Do nothing if the current log entry is already closed.
+                    if (watchEndTime > 0) {
+                        return;
+                    }
+
+                    // The current program has just ended. Create a (complete) log entry off the
+                    // current entry.
+                    ContentValues values = new ContentValues();
+                    values.put(TvContract.WatchedPrograms.WATCH_START_TIME_UTC_MILLIS,
+                            watchStartTime);
+                    values.put(TvContract.WatchedPrograms.WATCH_END_TIME_UTC_MILLIS, time);
+                    values.put(TvContract.WatchedPrograms.CHANNEL_ID, channelId);
+                    values.put(TvContract.WatchedPrograms.TITLE, title);
+                    values.put(TvContract.WatchedPrograms.START_TIME_UTC_MILLIS, startTime);
+                    values.put(TvContract.WatchedPrograms.END_TIME_UTC_MILLIS, endTime);
+                    values.put(TvContract.WatchedPrograms.DESCRIPTION, description);
+                    mContentResolver.insert(TvContract.WatchedPrograms.CONTENT_URI, values);
+                }
+            } finally {
+                if (cursor != null) {
+                    cursor.close();
+                }
+            }
+            // Re-open the current log entry with the next program information.
+            onOpenEntry(uri, channelId, time);
+        }
+
+        private void onCloseEntry(Uri uri, long watchEndTime) {
+            ContentValues values = new ContentValues();
+            values.put(TvContract.WatchedPrograms.WATCH_END_TIME_UTC_MILLIS, watchEndTime);
+            mContentResolver.update(uri, values, null, null);
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 87953fe..3eb2d5f 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -25,6 +25,7 @@
 import android.app.IWallpaperManagerCallback;
 import android.app.PendingIntent;
 import android.app.WallpaperInfo;
+import android.app.WallpaperManager;
 import android.app.backup.BackupManager;
 import android.app.backup.WallpaperBackupHelper;
 import android.content.BroadcastReceiver;
@@ -844,13 +845,7 @@
         
         try {
             if (componentName == null) {
-                String defaultComponent = 
-                    mContext.getString(com.android.internal.R.string.default_wallpaper_component);
-                if (defaultComponent != null) {
-                    // See if there is a default wallpaper component specified
-                    componentName = ComponentName.unflattenFromString(defaultComponent);
-                    if (DEBUG) Slog.v(TAG, "Use default component wallpaper:" + componentName);
-                }
+                componentName = WallpaperManager.getDefaultWallpaperComponent(mContext);
                 if (componentName == null) {
                     // Fall back to static image wallpaper
                     componentName = IMAGE_WALLPAPER;
diff --git a/services/core/jni/Android.mk b/services/core/jni/Android.mk
index 1b3887c..51583a5 100644
--- a/services/core/jni/Android.mk
+++ b/services/core/jni/Android.mk
@@ -11,7 +11,9 @@
     $(LOCAL_REL_DIR)/com_android_server_connectivity_Vpn.cpp \
     $(LOCAL_REL_DIR)/com_android_server_ConsumerIrService.cpp \
     $(LOCAL_REL_DIR)/com_android_server_dreams_McuHal.cpp \
+    $(LOCAL_REL_DIR)/com_android_server_hdmi_HdmiCecController.cpp \
     $(LOCAL_REL_DIR)/com_android_server_hdmi_HdmiCecService.cpp \
+    $(LOCAL_REL_DIR)/com_android_server_hdmi_HdmiMhlController.cpp \
     $(LOCAL_REL_DIR)/com_android_server_input_InputApplicationHandle.cpp \
     $(LOCAL_REL_DIR)/com_android_server_input_InputManagerService.cpp \
     $(LOCAL_REL_DIR)/com_android_server_input_InputWindowHandle.cpp \
diff --git a/services/core/jni/com_android_server_hdmi_HdmiCecController.cpp b/services/core/jni/com_android_server_hdmi_HdmiCecController.cpp
new file mode 100644
index 0000000..f3e8f3c
--- /dev/null
+++ b/services/core/jni/com_android_server_hdmi_HdmiCecController.cpp
@@ -0,0 +1,83 @@
+/*
+ * 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 "HdmiCecControllerJni"
+
+#define LOG_NDEBUG 1
+
+#include "JNIHelp.h"
+
+#include <android_runtime/AndroidRuntime.h>
+#include <android_runtime/Log.h>
+#include <hardware/hdmi_cec.h>
+
+namespace android {
+
+static struct {
+    jmethodID handleMessage;
+} gHdmiCecControllerClassInfo;
+
+
+class HdmiCecController {
+public:
+    HdmiCecController(jobject callbacksObj);
+
+private:
+    static void onReceived(const hdmi_event_t* event, void* arg);
+
+    jobject mCallbacksObj;
+};
+
+HdmiCecController::HdmiCecController(jobject callbacksObj) :
+    mCallbacksObj(callbacksObj) {
+}
+
+// static
+void HdmiCecController::onReceived(const hdmi_event_t* event, void* arg) {
+    HdmiCecController* handler = static_cast<HdmiCecController*>(arg);
+    if (handler == NULL) {
+        return;
+    }
+
+    // TODO: propagate message to Java layer.
+}
+
+
+//------------------------------------------------------------------------------
+static jlong nativeInit(JNIEnv* env, jclass clazz, jobject callbacksObj) {
+    // TODO: initialize hal and pass it to controller if ready.
+
+    HdmiCecController* controller = new HdmiCecController(
+            env->NewGlobalRef(callbacksObj));
+
+    return reinterpret_cast<jlong>(controller);
+}
+
+static JNINativeMethod sMethods[] = {
+    /* name, signature, funcPtr */
+    { "nativeInit", "(Lcom/android/server/hdmi/HdmiCecController;)J",
+            (void *) nativeInit },
+};
+
+#define CLASS_PATH "com/android/server/hdmi/HdmiCecController"
+
+int register_android_server_hdmi_HdmiCecController(JNIEnv* env) {
+    int res = jniRegisterNativeMethods(env, CLASS_PATH, sMethods, NELEM(sMethods));
+    LOG_FATAL_IF(res < 0, "Unable to register native methods.");
+    return 0;
+}
+
+}  /* namespace android */
diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp
index bf9f7f4..1feb325 100644
--- a/services/core/jni/onload.cpp
+++ b/services/core/jni/onload.cpp
@@ -38,7 +38,9 @@
 int register_android_server_location_FlpHardwareProvider(JNIEnv* env);
 int register_android_server_connectivity_Vpn(JNIEnv* env);
 int register_android_server_dreams_McuHal(JNIEnv* env);
+int register_android_server_hdmi_HdmiCecController(JNIEnv* env);
 int register_android_server_hdmi_HdmiCecService(JNIEnv* env);
+int register_android_server_hdmi_HdmiMhlController(JNIEnv* env);
 };
 
 using namespace android;
@@ -72,7 +74,10 @@
     register_android_server_ConsumerIrService(env);
     register_android_server_dreams_McuHal(env);
     register_android_server_BatteryStatsService(env);
+    register_android_server_hdmi_HdmiCecController(env);
+    // TODO: remove this once replaces HdmiCecService with HdmiControlService.
     register_android_server_hdmi_HdmiCecService(env);
+    register_android_server_hdmi_HdmiMhlController(env);
 
     return JNI_VERSION_1_4;
 }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index b82a126..f1ee280 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -3118,4 +3118,24 @@
             }
         }
     }
+
+    @Override
+    public void setUserRestriction(ComponentName who, String key, boolean enabled) {
+        final UserHandle userHandle = new UserHandle(UserHandle.getCallingUserId());
+
+        synchronized (this) {
+            if (who == null) {
+                throw new NullPointerException("ComponentName is null");
+            }
+            getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+
+            UserManager um = UserManager.get(mContext);
+            long id = Binder.clearCallingIdentity();
+            try {
+                um.setUserRestriction(key, enabled, userHandle);
+            } finally {
+                restoreCallingIdentity(id);
+            }
+        }
+    }
 }
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index f08d69f..08b1eba 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -29,6 +29,7 @@
 import android.content.pm.PackageManager;
 import android.content.res.Configuration;
 import android.media.AudioService;
+import android.os.Build;
 import android.os.Environment;
 import android.os.FactoryTest;
 import android.os.Handler;
@@ -61,6 +62,7 @@
 import com.android.server.content.ContentService;
 import com.android.server.display.DisplayManagerService;
 import com.android.server.dreams.DreamManagerService;
+import com.android.server.hdmi.HdmiControlService;
 import com.android.server.input.InputManagerService;
 import com.android.server.lights.LightsManager;
 import com.android.server.lights.LightsService;
@@ -114,6 +116,8 @@
             "com.android.server.devicepolicy.DevicePolicyManagerService$Lifecycle";
     private static final String APPWIDGET_SERVICE_CLASS =
             "com.android.server.appwidget.AppWidgetService";
+    private static final String VOICE_RECOGNITION_MANAGER_SERVICE_CLASS =
+            "com.android.server.voiceinteraction.VoiceInteractionManagerService";
     private static final String PRINT_MANAGER_SERVICE_CLASS =
             "com.android.server.print.PrintManagerService";
     private static final String USB_SERVICE_CLASS =
@@ -198,6 +202,10 @@
         // as efficient as possible with its memory usage.
         VMRuntime.getRuntime().setTargetHeapUtilization(0.8f);
 
+        // Some devices rely on runtime fingerprint generation, so make sure
+        // we've defined it before booting further.
+        Build.ensureFingerprintProperty();
+
         // Within the system server, it is an error to access Environment paths without
         // explicitly specifying a user.
         Environment.setUserRequired(true);
@@ -290,6 +298,7 @@
         // Activity manager runs the show.
         mActivityManagerService = mSystemServiceManager.startService(
                 ActivityManagerService.Lifecycle.class).getService();
+        mActivityManagerService.setSystemServiceManager(mSystemServiceManager);
     }
 
     private void startCoreServices() {
@@ -826,6 +835,15 @@
                 } catch (Throwable e) {
                     reportWtf("starting Recognition Service", e);
                 }
+
+                try {
+                    if (pm.hasSystemFeature(PackageManager.FEATURE_VOICE_RECOGNIZERS)) {
+                        Slog.i(TAG, "Voice Recognition Service");
+                        mSystemServiceManager.startService(VOICE_RECOGNITION_MANAGER_SERVICE_CLASS);
+                    }
+                } catch (Throwable e) {
+                    reportWtf("starting Voice Recognition Service", e);
+                }
             }
 
             try {
@@ -919,6 +937,12 @@
             }
 
             try {
+                mSystemServiceManager.startService(HdmiControlService.class);
+            } catch (Throwable e) {
+                reportWtf("starting HdmiControlService", e);
+            }
+
+            try {
                 Slog.i(TAG, "TvInputManagerService");
                 mSystemServiceManager.startService(TvInputManagerService.class);
             } catch (Throwable e) {
diff --git a/services/voiceinteraction/Android.mk b/services/voiceinteraction/Android.mk
new file mode 100644
index 0000000..c9e5dd0
--- /dev/null
+++ b/services/voiceinteraction/Android.mk
@@ -0,0 +1,12 @@
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := services.voiceinteraction
+
+LOCAL_SRC_FILES += \
+      $(call all-java-files-under,java)
+
+LOCAL_JAVA_LIBRARIES := services.core
+
+include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
new file mode 100644
index 0000000..045c0f6
--- /dev/null
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -0,0 +1,247 @@
+/*
+ * 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.voiceinteraction;
+
+import android.Manifest;
+import android.app.ActivityManager;
+import android.content.ComponentName;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.database.ContentObserver;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.service.voice.IVoiceInteractionService;
+import android.service.voice.IVoiceInteractionSession;
+import android.util.Slog;
+import com.android.internal.app.IVoiceInteractionManagerService;
+import com.android.internal.app.IVoiceInteractor;
+import com.android.internal.content.PackageMonitor;
+import com.android.internal.os.BackgroundThread;
+import com.android.server.SystemService;
+import com.android.server.UiThread;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+
+/**
+ * SystemService that publishes an IVoiceInteractionManagerService.
+ */
+public class VoiceInteractionManagerService extends SystemService {
+
+    static final String TAG = "VoiceInteractionManagerService";
+
+    final Context mContext;
+    final ContentResolver mResolver;
+
+    public VoiceInteractionManagerService(Context context) {
+        super(context);
+        mContext = context;
+        mResolver = context.getContentResolver();
+    }
+
+    @Override
+    public void onStart() {
+        publishBinderService(Context.VOICE_INTERACTION_MANAGER_SERVICE, mServiceStub);
+    }
+
+    @Override
+    public void onBootPhase(int phase) {
+        if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
+            mServiceStub.systemRunning(isSafeMode());
+        }
+    }
+
+    @Override
+    public void onSwitchUser(int userHandle) {
+        mServiceStub.switchUser(userHandle);
+    }
+
+    // implementation entry point and binder service
+    private final VoiceInteractionManagerServiceStub mServiceStub
+            = new VoiceInteractionManagerServiceStub();
+
+    class VoiceInteractionManagerServiceStub extends IVoiceInteractionManagerService.Stub {
+
+        VoiceInteractionManagerServiceImpl mImpl;
+
+        private boolean mSafeMode;
+        private int mCurUser;
+
+        public void systemRunning(boolean safeMode) {
+            mSafeMode = safeMode;
+
+            mPackageMonitor.register(mContext, BackgroundThread.getHandler().getLooper(),
+                    UserHandle.ALL, true);
+            new SettingsObserver(UiThread.getHandler());
+
+            synchronized (this) {
+                mCurUser = ActivityManager.getCurrentUser();
+                switchImplementationIfNeededLocked();
+            }
+        }
+
+        public void switchUser(int userHandle) {
+            synchronized (this) {
+                mCurUser = userHandle;
+                switchImplementationIfNeededLocked();
+            }
+        }
+
+        void switchImplementationIfNeededLocked() {
+            if (!mSafeMode) {
+                String curService = Settings.Secure.getStringForUser(
+                        mResolver, Settings.Secure.VOICE_INTERACTION_SERVICE, mCurUser);
+                ComponentName serviceComponent = null;
+                if (curService != null && !curService.isEmpty()) {
+                    try {
+                        serviceComponent = ComponentName.unflattenFromString(curService);
+                    } catch (RuntimeException e) {
+                        Slog.wtf(TAG, "Bad voice interaction service name " + curService, e);
+                        serviceComponent = null;
+                    }
+                }
+                if (mImpl == null || mImpl.mUser != mCurUser
+                        || !mImpl.mComponent.equals(serviceComponent)) {
+                    if (mImpl != null) {
+                        mImpl.shutdownLocked();
+                    }
+                    if (serviceComponent != null) {
+                        mImpl = new VoiceInteractionManagerServiceImpl(mContext,
+                                UiThread.getHandler(), this, mCurUser, serviceComponent);
+                        mImpl.startLocked();
+                    } else {
+                        mImpl = null;
+                    }
+                }
+            }
+        }
+
+        @Override
+        public void startVoiceActivity(Intent intent, String resolvedType,
+                IVoiceInteractionService service, Bundle args) {
+            synchronized (this) {
+                if (mImpl == null || service.asBinder() != mImpl.mService.asBinder()) {
+                    throw new SecurityException(
+                            "Caller is not the current voice interaction service");
+                }
+                final int callingPid = Binder.getCallingPid();
+                final int callingUid = Binder.getCallingUid();
+                final long caller = Binder.clearCallingIdentity();
+                try {
+                    mImpl.startVoiceActivityLocked(callingPid, callingUid,
+                            intent, resolvedType, args);
+                } finally {
+                    Binder.restoreCallingIdentity(caller);
+                }
+            }
+        }
+
+        @Override
+        public int deliverNewSession(IBinder token, IVoiceInteractionSession session,
+                IVoiceInteractor interactor) {
+            synchronized (this) {
+                if (mImpl == null) {
+                    Slog.w(TAG, "deliverNewSession without running voice interaction service");
+                    return ActivityManager.START_CANCELED;
+                }
+                final int callingPid = Binder.getCallingPid();
+                final int callingUid = Binder.getCallingUid();
+                final long caller = Binder.clearCallingIdentity();
+                try {
+                    return mImpl.deliverNewSessionLocked(callingPid, callingUid, token, session,
+                            interactor);
+                } finally {
+                    Binder.restoreCallingIdentity(caller);
+                }
+            }
+
+        }
+
+        @Override
+        public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+            if (mContext.checkCallingOrSelfPermission(Manifest.permission.DUMP)
+                    != PackageManager.PERMISSION_GRANTED) {
+                pw.println("Permission Denial: can't dump PowerManager from from pid="
+                        + Binder.getCallingPid()
+                        + ", uid=" + Binder.getCallingUid());
+                return;
+            }
+            synchronized (this) {
+                pw.println("VOICE INTERACTION MANAGER (dumpsys voiceinteraction)\n");
+                if (mImpl == null) {
+                    pw.println("  (No active implementation)");
+                    return;
+                }
+                mImpl.dumpLocked(fd, pw, args);
+            }
+        }
+
+        class SettingsObserver extends ContentObserver {
+            SettingsObserver(Handler handler) {
+                super(handler);
+                ContentResolver resolver = mContext.getContentResolver();
+                resolver.registerContentObserver(Settings.Secure.getUriFor(
+                        Settings.Secure.VOICE_INTERACTION_SERVICE), false, this);
+            }
+
+            @Override public void onChange(boolean selfChange) {
+                synchronized (VoiceInteractionManagerServiceStub.this) {
+                    switchImplementationIfNeededLocked();
+                }
+            }
+        }
+
+        PackageMonitor mPackageMonitor = new PackageMonitor() {
+            @Override
+            public boolean onHandleForceStop(Intent intent, String[] packages, int uid, boolean doit) {
+                return super.onHandleForceStop(intent, packages, uid, doit);
+            }
+
+            @Override
+            public void onHandleUserStop(Intent intent, int userHandle) {
+                super.onHandleUserStop(intent, userHandle);
+            }
+
+            @Override
+            public void onPackageDisappeared(String packageName, int reason) {
+                super.onPackageDisappeared(packageName, reason);
+            }
+
+            @Override
+            public void onPackageAppeared(String packageName, int reason) {
+                super.onPackageAppeared(packageName, reason);
+            }
+
+            @Override
+            public void onPackageModified(String packageName) {
+                super.onPackageModified(packageName);
+            }
+
+            @Override
+            public void onSomePackagesChanged() {
+                super.onSomePackagesChanged();
+            }
+        };
+    }
+}
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
new file mode 100644
index 0000000..6bbd1c1
--- /dev/null
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
@@ -0,0 +1,238 @@
+/*
+ * 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.voiceinteraction;
+
+import android.app.ActivityManager;
+import android.app.ActivityManagerNative;
+import android.app.IActivityManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.content.pm.PackageManager;
+import android.content.pm.ServiceInfo;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.service.voice.IVoiceInteractionService;
+import android.service.voice.IVoiceInteractionSession;
+import android.service.voice.IVoiceInteractionSessionService;
+import android.service.voice.VoiceInteractionService;
+import android.service.voice.VoiceInteractionServiceInfo;
+import android.util.Slog;
+import com.android.internal.app.IVoiceInteractor;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+class VoiceInteractionManagerServiceImpl {
+    final static String TAG = "VoiceInteractionServiceManager";
+
+    final boolean mValid;
+
+    final Context mContext;
+    final Handler mHandler;
+    final Object mLock;
+    final int mUser;
+    final ComponentName mComponent;
+    final IActivityManager mAm;
+    final VoiceInteractionServiceInfo mInfo;
+    final ComponentName mSessionComponentName;
+    boolean mBound = false;
+    IVoiceInteractionService mService;
+
+    SessionConnection mActiveSession;
+
+    final ServiceConnection mConnection = new ServiceConnection() {
+        @Override
+        public void onServiceConnected(ComponentName name, IBinder service) {
+            synchronized (mLock) {
+                mService = IVoiceInteractionService.Stub.asInterface(service);
+            }
+        }
+
+        @Override
+        public void onServiceDisconnected(ComponentName name) {
+            mService = null;
+        }
+    };
+
+    final class SessionConnection implements ServiceConnection {
+        final IBinder mToken = new Binder();
+        final Intent mIntent;
+        final String mResolvedType;
+        final Bundle mArgs;
+        boolean mBound;
+        IVoiceInteractionSessionService mService;
+        IVoiceInteractionSession mSession;
+        IVoiceInteractor mInteractor;
+
+        SessionConnection(Intent intent, String resolvedType, Bundle args) {
+            mIntent = intent;
+            mResolvedType = resolvedType;
+            mArgs = args;
+            Intent serviceIntent = new Intent(VoiceInteractionService.SERVICE_INTERFACE);
+            serviceIntent.setComponent(mSessionComponentName);
+            mBound = mContext.bindServiceAsUser(serviceIntent, this,
+                    Context.BIND_AUTO_CREATE, new UserHandle(mUser));
+            if (!mBound) {
+                Slog.w(TAG, "Failed binding to voice interaction session service " + mComponent);
+            }
+        }
+
+        @Override
+        public void onServiceConnected(ComponentName name, IBinder service) {
+            synchronized (mLock) {
+                mService = IVoiceInteractionSessionService.Stub.asInterface(service);
+                if (mActiveSession == this) {
+                    try {
+                        mService.newSession(mToken, mArgs);
+                    } catch (RemoteException e) {
+                        Slog.w(TAG, "Failed making new session", e);
+                    }
+                }
+            }
+        }
+
+        @Override
+        public void onServiceDisconnected(ComponentName name) {
+            mService = null;
+        }
+
+        public void cancel() {
+            if (mBound) {
+                mContext.unbindService(this);
+                mBound = false;
+                mService = null;
+                mSession = null;
+                mInteractor = null;
+            }
+        }
+
+        public void dump(String prefix, PrintWriter pw) {
+            pw.print(prefix); pw.print("mToken="); pw.println(mToken);
+            pw.print(prefix); pw.print("mIntent="); pw.println(mIntent);
+                    pw.print(" mResolvedType="); pw.println(mResolvedType);
+            pw.print(prefix); pw.print("mArgs="); pw.println(mArgs);
+            pw.print(prefix); pw.print("mBound="); pw.println(mBound);
+            if (mBound) {
+                pw.print(prefix); pw.print("mService="); pw.println(mService);
+                pw.print(prefix); pw.print("mSession="); pw.println(mSession);
+                pw.print(prefix); pw.print("mInteractor="); pw.println(mInteractor);
+            }
+        }
+    };
+
+    VoiceInteractionManagerServiceImpl(Context context, Handler handler, Object lock,
+            int userHandle, ComponentName service) {
+        mContext = context;
+        mHandler = handler;
+        mLock = lock;
+        mUser = userHandle;
+        mComponent = service;
+        mAm = ActivityManagerNative.getDefault();
+        VoiceInteractionServiceInfo info;
+        try {
+            info = new VoiceInteractionServiceInfo(context.getPackageManager(), service);
+        } catch (PackageManager.NameNotFoundException e) {
+            Slog.w(TAG, "Voice interaction service not found: " + service);
+            mInfo = null;
+            mSessionComponentName = null;
+            mValid = false;
+            return;
+        }
+        mInfo = info;
+        if (mInfo.getParseError() != null) {
+            Slog.w(TAG, "Bad voice interaction service: " + mInfo.getParseError());
+            mSessionComponentName = null;
+            mValid = false;
+            return;
+        }
+        mValid = true;
+        mSessionComponentName = new ComponentName(service.getPackageName(),
+                mInfo.getSessionService());
+    }
+
+    public void startVoiceActivityLocked(int callingPid, int callingUid, Intent intent,
+            String resolvedType, Bundle args) {
+        if (mActiveSession != null) {
+            mActiveSession.cancel();
+            mActiveSession = null;
+        }
+        mActiveSession = new SessionConnection(intent, resolvedType, args);
+        intent.addCategory(Intent.CATEGORY_VOICE);
+        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
+    }
+
+    public int deliverNewSessionLocked(int callingPid, int callingUid, IBinder token,
+            IVoiceInteractionSession session, IVoiceInteractor interactor) {
+        try {
+            if (mActiveSession == null || token != mActiveSession.mToken) {
+                Slog.w(TAG, "deliverNewSession does not match active session");
+                return ActivityManager.START_CANCELED;
+            }
+            mActiveSession.mSession = session;
+            mActiveSession.mInteractor = interactor;
+            return mAm.startVoiceActivity(mComponent.getPackageName(), callingPid, callingUid,
+                    mActiveSession.mIntent, mActiveSession.mResolvedType,
+                    mActiveSession.mSession, mActiveSession.mInteractor,
+                    0, null, null, null, mUser);
+        } catch (RemoteException e) {
+            throw new IllegalStateException("Unexpected remote error", e);
+        }
+    }
+
+    public void dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args) {
+        if (!mValid) {
+            pw.print("  NOT VALID: ");
+            if (mInfo == null) {
+                pw.println("no info");
+            } else {
+                pw.println(mInfo.getParseError());
+            }
+            return;
+        }
+        pw.print("  mComponent="); pw.println(mComponent.flattenToShortString());
+        pw.print("  Session service="); pw.println(mInfo.getSessionService());
+        pw.print("  Settings activity="); pw.println(mInfo.getSettingsActivity());
+        pw.print("  mBound="); pw.print(mBound);  pw.print(" mService="); pw.println(mService);
+        if (mActiveSession != null) {
+            pw.println("  Active session:");
+            mActiveSession.dump("    ", pw);
+        }
+    }
+
+    void startLocked() {
+        Intent intent = new Intent(VoiceInteractionService.SERVICE_INTERFACE);
+        intent.setComponent(mComponent);
+        mBound = mContext.bindServiceAsUser(intent, mConnection,
+                Context.BIND_AUTO_CREATE, new UserHandle(mUser));
+        if (!mBound) {
+            Slog.w(TAG, "Failed binding to voice interaction service " + mComponent);
+        }
+    }
+
+    void shutdownLocked() {
+        if (mBound) {
+            mContext.unbindService(mConnection);
+            mBound = false;
+        }
+    }
+}
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index d5c7caa..407a8d1 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -1094,6 +1094,10 @@
     public static final int SIM_STATE_NETWORK_LOCKED = 4;
     /** SIM card state: Ready */
     public static final int SIM_STATE_READY = 5;
+    /** SIM card state: SIM Card Error, Sim Card is present but faulty
+     *@hide
+     */
+    public static final int SIM_STATE_CARD_IO_ERROR = 6;
 
     /**
      * @return true if a ICC card is present
@@ -1120,6 +1124,7 @@
      * @see #SIM_STATE_PUK_REQUIRED
      * @see #SIM_STATE_NETWORK_LOCKED
      * @see #SIM_STATE_READY
+     * @see #SIM_STATE_CARD_IO_ERROR
      */
     public int getSimState() {
         String prop = SystemProperties.get(TelephonyProperties.PROPERTY_SIM_STATE);
@@ -1138,6 +1143,9 @@
         else if ("READY".equals(prop)) {
             return SIM_STATE_READY;
         }
+        else if ("CARD_IO_ERROR".equals(prop)) {
+            return SIM_STATE_CARD_IO_ERROR;
+        }
         else {
             return SIM_STATE_UNKNOWN;
         }
diff --git a/telephony/java/com/android/internal/telephony/IccCardConstants.java b/telephony/java/com/android/internal/telephony/IccCardConstants.java
index 236bb2f..8029713 100644
--- a/telephony/java/com/android/internal/telephony/IccCardConstants.java
+++ b/telephony/java/com/android/internal/telephony/IccCardConstants.java
@@ -28,6 +28,8 @@
     public static final String INTENT_VALUE_ICC_NOT_READY = "NOT_READY";
     /* ABSENT means ICC is missing */
     public static final String INTENT_VALUE_ICC_ABSENT = "ABSENT";
+    /* CARD_IO_ERROR means for three consecutive times there was SIM IO error */
+    static public final String INTENT_VALUE_ICC_CARD_IO_ERROR = "CARD_IO_ERROR";
     /* LOCKED means ICC is locked by pin or by network */
     public static final String INTENT_VALUE_ICC_LOCKED = "LOCKED";
     /* READY means ICC is ready to access */
@@ -63,7 +65,8 @@
         NETWORK_LOCKED,
         READY,
         NOT_READY,
-        PERM_DISABLED;
+        PERM_DISABLED,
+        CARD_IO_ERROR;
 
         public boolean isPinLocked() {
             return ((this == PIN_REQUIRED) || (this == PUK_REQUIRED));
@@ -72,7 +75,7 @@
         public boolean iccCardExist() {
             return ((this == PIN_REQUIRED) || (this == PUK_REQUIRED)
                     || (this == NETWORK_LOCKED) || (this == READY)
-                    || (this == PERM_DISABLED));
+                    || (this == PERM_DISABLED) || (this == CARD_IO_ERROR));
         }
     }
 }
diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml
index ac741e7..5c2583b 100644
--- a/tests/HwAccelerationTest/AndroidManifest.xml
+++ b/tests/HwAccelerationTest/AndroidManifest.xml
@@ -873,9 +873,6 @@
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
                 <category android:name="com.android.test.hwui.TEST" />
-
-                <category android:name="android.intent.category.DEFAULT" />
-                <category android:name="android.intent.category.LAUNCHER" />
             </intent-filter>
         </activity>
     </application>
diff --git a/tests/VoiceInteraction/Android.mk b/tests/VoiceInteraction/Android.mk
new file mode 100644
index 0000000..8decca7
--- /dev/null
+++ b/tests/VoiceInteraction/Android.mk
@@ -0,0 +1,10 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_PACKAGE_NAME := VoiceInteraction
+
+include $(BUILD_PACKAGE)
diff --git a/tests/VoiceInteraction/AndroidManifest.xml b/tests/VoiceInteraction/AndroidManifest.xml
new file mode 100644
index 0000000..ac0f701
--- /dev/null
+++ b/tests/VoiceInteraction/AndroidManifest.xml
@@ -0,0 +1,33 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="com.android.test.voiceinteraction">
+
+    <application>
+        <activity android:name="VoiceInteractionMain" android:label="Voice Interaction">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+        <service android:name="MainInteractionService"
+                android:permission="android.permission.BIND_VOICE_INTERACTION"
+                android:process=":interactor">
+            <meta-data android:name="android.voice_interaction"
+                       android:resource="@xml/interaction_service" />
+            <intent-filter>
+                <action android:name="android.service.voice.VoiceInteractionService" />
+            </intent-filter>
+        </service>
+        <service android:name="MainInteractionSessionService"
+                android:permission="android.permission.BIND_VOICE_INTERACTION"
+                android:process=":session">
+        </service>
+        <activity android:name="TestInteractionActivity" android:label="Voice Interaction Target">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <category android:name="android.intent.category.VOICE" />
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>
diff --git a/tests/VoiceInteraction/res/layout/main.xml b/tests/VoiceInteraction/res/layout/main.xml
new file mode 100644
index 0000000..3d7a418
--- /dev/null
+++ b/tests/VoiceInteraction/res/layout/main.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical"
+    >
+
+    <Button android:id="@+id/start"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="@string/start"
+        />
+
+</LinearLayout>
+
+
diff --git a/tests/VoiceInteraction/res/layout/test_interaction.xml b/tests/VoiceInteraction/res/layout/test_interaction.xml
new file mode 100644
index 0000000..2abf65194
--- /dev/null
+++ b/tests/VoiceInteraction/res/layout/test_interaction.xml
@@ -0,0 +1,37 @@
+<?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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical"
+    >
+
+    <TextView android:layout_width="wrap_content" android:layout_height="wrap_content"
+        android:textAppearance="?android:attr/textAppearanceMedium"
+        android:text="We are interacting!"
+        />
+
+    <TextView android:id="@+id/log"
+        android:layout_width="match_parent"
+        android:layout_height="0px"
+        android:layout_weight="1"
+        android:layout_marginTop="10dp"
+        android:textSize="12sp"
+        android:textColor="#ffffffff"
+        />
+
+</LinearLayout>
diff --git a/tests/VoiceInteraction/res/values/strings.xml b/tests/VoiceInteraction/res/values/strings.xml
new file mode 100644
index 0000000..12edb31
--- /dev/null
+++ b/tests/VoiceInteraction/res/values/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<resources>
+
+    <string name="start">Start!</string>
+
+</resources>
+
diff --git a/tests/VoiceInteraction/res/xml/interaction_service.xml b/tests/VoiceInteraction/res/xml/interaction_service.xml
new file mode 100644
index 0000000..45bd994d
--- /dev/null
+++ b/tests/VoiceInteraction/res/xml/interaction_service.xml
@@ -0,0 +1,21 @@
+<?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.
+ */
+-->
+
+<voice-interaction-service xmlns:android="http://schemas.android.com/apk/res/android"
+    android:sessionService="com.android.test.voiceinteraction.MainInteractionSessionService" />
diff --git a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionService.java b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionService.java
new file mode 100644
index 0000000..008d97b
--- /dev/null
+++ b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionService.java
@@ -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 com.android.test.voiceinteraction;
+
+import android.content.Intent;
+import android.service.voice.VoiceInteractionService;
+import android.util.Log;
+
+public class MainInteractionService extends VoiceInteractionService {
+    static final String TAG = "MainInteractionService";
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        Log.i(TAG, "Creating " + this);
+    }
+
+    @Override
+    public int onStartCommand(Intent intent, int flags, int startId) {
+        startVoiceActivity(new Intent(this, TestInteractionActivity.class), null);
+        stopSelf(startId);
+        return START_NOT_STICKY;
+    }
+}
diff --git a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionSession.java b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionSession.java
new file mode 100644
index 0000000..0fc563b
--- /dev/null
+++ b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionSession.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.test.voiceinteraction;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.service.voice.VoiceInteractionSession;
+import android.util.Log;
+
+public class MainInteractionSession extends VoiceInteractionSession {
+    static final String TAG = "MainInteractionSession";
+
+    final Bundle mArgs;
+
+    MainInteractionSession(Context context, Bundle args) {
+        super(context);
+        mArgs = args;
+    }
+
+    @Override
+    public boolean[] onGetSupportedCommands(Caller caller, String[] commands) {
+        return new boolean[commands.length];
+    }
+
+    @Override
+    public void onConfirm(Caller caller, Request request, String prompt, Bundle extras) {
+        Log.i(TAG, "onConform: prompt=" + prompt + " extras=" + extras);
+        request.sendConfirmResult(true, null);
+    }
+
+    @Override
+    public void onCommand(Caller caller, Request request, String command, Bundle extras) {
+        Log.i(TAG, "onCommand: command=" + command + " extras=" + extras);
+        request.sendCommandResult(true, null);
+    }
+
+    @Override
+    public void onCancel(Request request) {
+        Log.i(TAG, "onCancel");
+        request.sendCancelResult();
+    }
+}
diff --git a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionSessionService.java b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionSessionService.java
new file mode 100644
index 0000000..8864d71
--- /dev/null
+++ b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionSessionService.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.test.voiceinteraction;
+
+import android.os.Bundle;
+import android.service.voice.VoiceInteractionSession;
+import android.service.voice.VoiceInteractionSessionService;
+
+public class MainInteractionSessionService extends VoiceInteractionSessionService {
+    @Override
+    public VoiceInteractionSession onNewSession(Bundle args) {
+        return new MainInteractionSession(this, args);
+    }
+}
diff --git a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/TestInteractionActivity.java b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/TestInteractionActivity.java
new file mode 100644
index 0000000..9c772ff
--- /dev/null
+++ b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/TestInteractionActivity.java
@@ -0,0 +1,68 @@
+/*
+ * 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.test.voiceinteraction;
+
+import android.app.Activity;
+import android.app.VoiceInteractor;
+import android.os.Bundle;
+import android.util.Log;
+
+public class TestInteractionActivity extends Activity {
+    static final String TAG = "TestInteractionActivity";
+
+    VoiceInteractor mInteractor;
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        if (!isVoiceInteraction()) {
+            Log.w(TAG, "Not running as a voice interaction!");
+            finish();
+            return;
+        }
+
+        setContentView(R.layout.test_interaction);
+
+        mInteractor = getVoiceInteractor();
+        VoiceInteractor.ConfirmationRequest req = new VoiceInteractor.ConfirmationRequest(
+                "This is a confirmation", null) {
+            @Override
+            public void onCancel() {
+                Log.i(TAG, "Canceled!");
+                getActivity().finish();
+            }
+
+            @Override
+            public void onConfirmationResult(boolean confirmed, Bundle result) {
+                Log.i(TAG, "Confirmation result: confirmed=" + confirmed + " result=" + result);
+                getActivity().finish();
+            }
+        };
+        mInteractor.submitRequest(req);
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+    }
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+    }
+}
diff --git a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/VoiceInteractionMain.java b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/VoiceInteractionMain.java
new file mode 100644
index 0000000..5d212a4
--- /dev/null
+++ b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/VoiceInteractionMain.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.test.voiceinteraction;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.View;
+
+public class VoiceInteractionMain extends Activity {
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        setContentView(R.layout.main);
+        findViewById(R.id.start).setOnClickListener(mStartListener);
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+    }
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+    }
+
+    View.OnClickListener mStartListener = new View.OnClickListener() {
+        public void onClick(View v) {
+            startService(new Intent(VoiceInteractionMain.this, MainInteractionService.class));
+        }
+    };
+}
diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp
index 6d9d62e..d0581f6 100644
--- a/tools/aapt/Resource.cpp
+++ b/tools/aapt/Resource.cpp
@@ -2462,7 +2462,7 @@
 
 status_t
 writeProguardForXml(ProguardKeepSet* keep, const sp<AaptFile>& layoutFile,
-        const char* startTag, const KeyedVector<String8, Vector<NamespaceAttributePair> >* tagAttrPairs)
+        const Vector<String8>& startTags, const KeyedVector<String8, Vector<NamespaceAttributePair> >* tagAttrPairs)
 {
     status_t err;
     ResXMLTree tree;
@@ -2476,15 +2476,18 @@
 
     tree.restart();
 
-    if (startTag != NULL) {
+    if (!startTags.isEmpty()) {
         bool haveStart = false;
         while ((code=tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) {
             if (code != ResXMLTree::START_TAG) {
                 continue;
             }
             String8 tag(tree.getElementName(&len));
-            if (tag == startTag) {
-                haveStart = true;
+            const size_t numStartTags = startTags.size();
+            for (size_t i = 0; i < numStartTags; i++) {
+                if (tag == startTags[i]) {
+                    haveStart = true;
+                }
             }
             break;
         }
@@ -2571,15 +2574,17 @@
     for (size_t k=0; k<K; k++) {
         const sp<AaptDir>& d = dirs.itemAt(k);
         const String8& dirName = d->getLeaf();
+        Vector<String8> startTags;
         const char* startTag = NULL;
         const KeyedVector<String8, Vector<NamespaceAttributePair> >* tagAttrPairs = NULL;
         if ((dirName == String8("layout")) || (strncmp(dirName.string(), "layout-", 7) == 0)) {
             tagAttrPairs = &kLayoutTagAttrPairs;
         } else if ((dirName == String8("xml")) || (strncmp(dirName.string(), "xml-", 4) == 0)) {
-            startTag = "PreferenceScreen";
+            startTags.add(String8("PreferenceScreen"));
+            startTags.add(String8("preference-headers"));
             tagAttrPairs = &kXmlTagAttrPairs;
         } else if ((dirName == String8("menu")) || (strncmp(dirName.string(), "menu-", 5) == 0)) {
-            startTag = "menu";
+            startTags.add(String8("menu"));
             tagAttrPairs = NULL;
         } else {
             continue;
@@ -2592,7 +2597,7 @@
             const DefaultKeyedVector<AaptGroupEntry, sp<AaptFile> >& files = group->getFiles();
             const size_t M = files.size();
             for (size_t j=0; j<M; j++) {
-                err = writeProguardForXml(keep, files.valueAt(j), startTag, tagAttrPairs);
+                err = writeProguardForXml(keep, files.valueAt(j), startTags, tagAttrPairs);
                 if (err < 0) {
                     return err;
                 }