Merge "Delay settings panel visibility until touch slop exceeded."
diff --git a/Android.mk b/Android.mk
index be7e055..df8fbd9 100644
--- a/Android.mk
+++ b/Android.mk
@@ -180,6 +180,7 @@
 	core/java/android/os/IUserManager.aidl \
 	core/java/android/os/IVibratorService.aidl \
 	core/java/android/service/notification/INotificationListener.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 +198,8 @@
 	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/wallpaper/IWallpaperConnection.aidl \
 	core/java/android/service/wallpaper/IWallpaperEngine.aidl \
 	core/java/android/service/wallpaper/IWallpaperService.aidl \
@@ -230,6 +233,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 37d2345..81362f1 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";
@@ -3202,6 +3203,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 +3215,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 +4841,23 @@
     field public static final int MODE_NIGHT_YES = 2; // 0x2
   }
 
+  public class VoiceInteractor {
+    method public android.app.VoiceInteractor.Request startCommand(android.app.VoiceInteractor.Callback, java.lang.String, android.os.Bundle);
+    method public android.app.VoiceInteractor.Request startConfirmation(android.app.VoiceInteractor.Callback, java.lang.String, android.os.Bundle);
+    method public boolean[] supportsCommands(java.lang.String[]);
+  }
+
+  public static class VoiceInteractor.Callback {
+    ctor public VoiceInteractor.Callback();
+    method public void onCancel(android.app.VoiceInteractor.Request);
+    method public void onCommandResult(android.app.VoiceInteractor.Request, android.os.Bundle);
+    method public void onConfirmationResult(android.app.VoiceInteractor.Request, boolean, android.os.Bundle);
+  }
+
+  public static class VoiceInteractor.Request {
+    method public void cancel();
+  }
+
   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 +4967,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);
@@ -6997,6 +7019,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";
@@ -10023,11 +10046,16 @@
     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 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 {
@@ -10750,7 +10778,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();
@@ -13377,6 +13405,18 @@
     ctor public DeniedByServerException(java.lang.String);
   }
 
+  public final class DngCreator {
+    ctor public DngCreator(android.hardware.camera2.CameraCharacteristics, android.hardware.camera2.CaptureResult);
+    method public android.media.DngCreator setDescription(java.lang.String);
+    method public android.media.DngCreator setLocation(android.location.Location);
+    method public android.media.DngCreator setOrientation(int);
+    method public android.media.DngCreator setThumbnail(android.graphics.Bitmap);
+    method public android.media.DngCreator setThumbnail(android.media.Image);
+    method public void writeByteBuffer(java.io.OutputStream, java.nio.ByteBuffer, int, long) throws java.io.IOException;
+    method public void writeImage(java.io.OutputStream, android.media.Image) throws java.io.IOException;
+    method public void writeInputStream(java.io.OutputStream, java.io.InputStream, int, long) throws java.io.IOException;
+  }
+
   public class ExifInterface {
     ctor public ExifInterface(java.lang.String) throws java.io.IOException;
     method public double getAltitude(double);
@@ -24866,6 +24906,36 @@
 
 }
 
+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.service.voice.VoiceInteractionSession);
+    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(android.os.Bundle);
+    method public void sendConfirmResult(boolean, 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/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 599a608..197eaf8 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, 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 88eae7f..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;
@@ -3003,7 +3008,9 @@
                 int h;
                 if (w < 0) {
                     Resources res = r.activity.getResources();
-                    if (SystemProperties.getBoolean("persist.recents.use_alternate", false)) {
+                    Configuration config = res.getConfiguration();
+                    boolean useAlternateRecents = (config.smallestScreenWidthDp < 600);
+                    if (useAlternateRecents) {
                         int wId = com.android.internal.R.dimen.recents_thumbnail_width;
                         int hId = com.android.internal.R.dimen.recents_thumbnail_height;
                         mThumbnailWidth = w = res.getDimensionPixelSize(wId);
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/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..045fab1 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -22,6 +22,8 @@
 import android.app.Notification;
 import android.content.ComponentName;
 import android.content.Intent;
+import android.service.notification.Condition;
+import android.service.notification.IConditionProvider;
 import android.service.notification.INotificationListener;
 import android.service.notification.ZenModeConfig;
 
@@ -53,4 +55,5 @@
 
     ZenModeConfig getZenModeConfig();
     boolean setZenModeConfig(in ZenModeConfig config);
+    void notifyCondition(in IConditionProvider provider, in Condition condition);
 }
\ 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..6820dfd
--- /dev/null
+++ b/core/java/android/app/VoiceInteractor.java
@@ -0,0 +1,235 @@
+/*
+ * 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.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 IVoiceInteractor mInteractor;
+    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_CONFIRMATION_RESULT:
+                    if (DEBUG) Log.d(TAG, "onConfirmResult: req="
+                            + ((IVoiceInteractorRequest)args.arg2).asBinder()
+                            + " confirmed=" + msg.arg1 + " result=" + args.arg3);
+                    ((Callback)args.arg1).onConfirmationResult(
+                            findRequest((IVoiceInteractorRequest)args.arg2),
+                            msg.arg1 != 0, (Bundle)args.arg3);
+                    break;
+                case MSG_COMMAND_RESULT:
+                    if (DEBUG) Log.d(TAG, "onCommandResult: req="
+                            + ((IVoiceInteractorRequest)args.arg2).asBinder()
+                            + " result=" + args.arg2);
+                    ((Callback)args.arg1).onCommandResult(
+                            findRequest((IVoiceInteractorRequest) args.arg2),
+                            (Bundle) args.arg3);
+                    break;
+                case MSG_CANCEL_RESULT:
+                    if (DEBUG) Log.d(TAG, "onCancelResult: req="
+                            + ((IVoiceInteractorRequest)args.arg2).asBinder());
+                    ((Callback)args.arg1).onCancel(
+                            findRequest((IVoiceInteractorRequest) args.arg2));
+                    break;
+            }
+        }
+    };
+
+    final WeakHashMap<IBinder, Request> mActiveRequests = new WeakHashMap<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 class Request {
+        final IVoiceInteractorRequest mRequestInterface;
+
+        Request(IVoiceInteractorRequest requestInterface) {
+            mRequestInterface = requestInterface;
+        }
+
+        public void cancel() {
+            try {
+                mRequestInterface.cancel();
+            } catch (RemoteException e) {
+                Log.w(TAG, "Voice interactor has died", e);
+            }
+        }
+    }
+
+    public static class Callback {
+        VoiceInteractor mInteractor;
+
+        final IVoiceInteractorCallback.Stub mWrapper = new IVoiceInteractorCallback.Stub() {
+            @Override
+            public void deliverConfirmationResult(IVoiceInteractorRequest request, boolean confirmed,
+                    Bundle result) {
+                mInteractor.mHandlerCaller.sendMessage(mInteractor.mHandlerCaller.obtainMessageIOOO(
+                        MSG_CONFIRMATION_RESULT, confirmed ? 1 : 0, Callback.this, request,
+                        result));
+            }
+
+            @Override
+            public void deliverCommandResult(IVoiceInteractorRequest request, Bundle result) {
+                mInteractor.mHandlerCaller.sendMessage(mInteractor.mHandlerCaller.obtainMessageOOO(
+                        MSG_COMMAND_RESULT, Callback.this, request, result));
+            }
+
+            @Override
+            public void deliverCancel(IVoiceInteractorRequest request) throws RemoteException {
+                mInteractor.mHandlerCaller.sendMessage(mInteractor.mHandlerCaller.obtainMessageOO(
+                        MSG_CANCEL_RESULT, Callback.this, request));
+            }
+        };
+
+        public void onConfirmationResult(Request request, boolean confirmed, Bundle result) {
+        }
+
+        public void onCommandResult(Request request, Bundle result) {
+        }
+
+        public void onCancel(Request request) {
+        }
+    }
+
+    VoiceInteractor(Context context, IVoiceInteractor interactor, Looper looper) {
+        mContext = context;
+        mInteractor = interactor;
+        mHandlerCaller = new HandlerCaller(context, looper, mHandlerCallerCallback, true);
+    }
+
+    Request storeRequest(IVoiceInteractorRequest request) {
+        synchronized (mActiveRequests) {
+            Request req = new Request(request);
+            mActiveRequests.put(request.asBinder(), req);
+            return req;
+        }
+    }
+
+    Request findRequest(IVoiceInteractorRequest request) {
+        synchronized (mActiveRequests) {
+            Request req = mActiveRequests.get(request.asBinder());
+            if (req == null) {
+                throw new IllegalStateException("Received callback without active request: "
+                        + request);
+            }
+            return req;
+        }
+    }
+
+    /**
+     * Asynchronously 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 by calling the
+     * {@link Callback#onConfirmationResult Callback.onConfirmationResult} method.
+     *
+     * 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 informed consent.
+     * @param callback Required callback target for interaction results.
+     * @param prompt Optional confirmation text to read to the user as the action being confirmed.
+     * @param extras Additional optional information.
+     * @return Returns a new {@link Request} object representing this operation.
+     */
+    public Request startConfirmation(Callback callback, String prompt, Bundle extras) {
+        try {
+            callback.mInteractor = this;
+            Request req = storeRequest(mInteractor.startConfirmation(
+                    mContext.getOpPackageName(), callback.mWrapper, prompt, extras));
+            if (DEBUG) Log.d(TAG, "startConfirmation: req=" + req.mRequestInterface.asBinder()
+                    + " prompt=" + prompt + " extras=" + extras);
+            return req;
+        } catch (RemoteException e) {
+            throw new RuntimeException("Voice interactor has died", e);
+        }
+    }
+
+    /**
+     * Asynchronously executes 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 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.)
+     * The result of the command will be returned by calling the
+     * {@link Callback#onCommandResult Callback.onCommandResult} method.
+     *
+     * @param callback Required callback target for interaction results.
+     * @param command
+     * @param extras
+     * @return Returns a new {@link Request} object representing this operation.
+     */
+    public Request startCommand(Callback callback, String command, Bundle extras) {
+        try {
+            callback.mInteractor = this;
+            Request req = storeRequest(mInteractor.startCommand(
+                    mContext.getOpPackageName(), callback.mWrapper, command, extras));
+            if (DEBUG) Log.d(TAG, "startCommand: req=" + req.mRequestInterface.asBinder()
+                    + " command=" + command + " extras=" + extras);
+            return req;
+        } catch (RemoteException e) {
+            throw new RuntimeException("Voice interactor has died", e);
+        }
+    }
+
+    /**
+     * 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/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index d7170e8..d173f19 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -1957,4 +1957,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/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/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 8b6ca83..8f19f01 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -50,6 +50,7 @@
 import java.security.spec.InvalidKeySpecException;
 import java.security.spec.X509EncodedKeySpec;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Enumeration;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -147,8 +148,7 @@
     private String[] mSeparateProcesses;
     private boolean mOnlyCoreApps;
     private static final int SDK_VERSION = Build.VERSION.SDK_INT;
-    private static final String SDK_CODENAME = "REL".equals(Build.VERSION.CODENAME)
-            ? null : Build.VERSION.CODENAME;
+    private static final String[] SDK_CODENAMES = Build.VERSION.ACTIVE_CODENAMES;
 
     private int mParseError = PackageManager.INSTALL_SUCCEEDED;
 
@@ -1200,10 +1200,18 @@
                     sa.recycle();
 
                     if (minCode != null) {
-                        if (!minCode.equals(SDK_CODENAME)) {
-                            if (SDK_CODENAME != null) {
+                        boolean allowedCodename = false;
+                        for (String codename : SDK_CODENAMES) {
+                            if (minCode.equals(codename)) {
+                                allowedCodename = true;
+                                break;
+                            }
+                        }
+                        if (!allowedCodename) {
+                            if (SDK_CODENAMES.length > 0) {
                                 outError[0] = "Requires development platform " + minCode
-                                        + " (current platform is " + SDK_CODENAME + ")";
+                                        + " (current platform is any of "
+                                        + Arrays.toString(SDK_CODENAMES) + ")";
                             } else {
                                 outError[0] = "Requires development platform " + minCode
                                         + " but this is a release platform.";
@@ -1219,10 +1227,18 @@
                     }
                     
                     if (targetCode != null) {
-                        if (!targetCode.equals(SDK_CODENAME)) {
-                            if (SDK_CODENAME != null) {
+                        boolean allowedCodename = false;
+                        for (String codename : SDK_CODENAMES) {
+                            if (targetCode.equals(codename)) {
+                                allowedCodename = true;
+                                break;
+                            }
+                        }
+                        if (!allowedCodename) {
+                            if (SDK_CODENAMES.length > 0) {
                                 outError[0] = "Requires development platform " + targetCode
-                                        + " (current platform is " + SDK_CODENAME + ")";
+                                        + " (current platform is any of "
+                                        + Arrays.toString(SDK_CODENAMES) + ")";
                             } else {
                                 outError[0] = "Requires development platform " + targetCode
                                         + " but this is a release platform.";
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 19be2c8..11e3795 100644
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -118,14 +118,22 @@
          */
         public static final String CODENAME = getString("ro.build.version.codename");
 
+        private static final String[] ALL_CODENAMES
+                = getString("ro.build.version.all_codenames").split(",");
+
         /**
-         * The SDK version to use when accessing resources.
-         * Use the current SDK version code.  If we are a development build,
-         * also allow the previous SDK version + 1.
          * @hide
          */
-        public static final int RESOURCES_SDK_INT = SDK_INT
-                + ("REL".equals(CODENAME) ? 0 : 1);
+        public static final String[] ACTIVE_CODENAMES = "REL".equals(ALL_CODENAMES[0])
+                ? new String[0] : ALL_CODENAMES;
+
+        /**
+         * The SDK version to use when accessing resources.
+         * Use the current SDK version code.  For every active development codename
+         * we are operating under, we bump the assumed resource platform version by 1.
+         * @hide
+         */
+        public static final int RESOURCES_SDK_INT = SDK_INT + ACTIVE_CODENAMES.length;
     }
 
     /**
diff --git a/core/java/android/os/RecoverySystem.java b/core/java/android/os/RecoverySystem.java
index f671ed9..cdde4c7 100644
--- a/core/java/android/os/RecoverySystem.java
+++ b/core/java/android/os/RecoverySystem.java
@@ -375,6 +375,7 @@
         final ConditionVariable condition = new ConditionVariable();
 
         Intent intent = new Intent("android.intent.action.MASTER_CLEAR_NOTIFICATION");
+        intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
         context.sendOrderedBroadcastAsUser(intent, UserHandle.OWNER,
                 android.Manifest.permission.MASTER_CLEAR,
                 new BroadcastReceiver() {
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..b578b48 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
@@ -3311,6 +3318,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 +4523,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..cfd40f3
--- /dev/null
+++ b/core/java/android/service/notification/Condition.java
@@ -0,0 +1,119 @@
+/**
+ * Copyright (c) 2014, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.notification;
+
+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 int FLAG_RELEVANT_NOW = 1 << 0;
+    public static final int FLAG_RELEVANT_ALWAYS = 1 << 1;
+
+    public final Uri id;
+    public String caption;
+    public boolean state;
+    public int flags;
+
+
+    public Condition(Uri id, String caption, boolean state, int flags) {
+        if (id == null) throw new IllegalArgumentException("id is required");
+        if (caption == null) throw new IllegalArgumentException("caption is required");
+        this.id = id;
+        this.caption = caption;
+        this.state = state;
+        this.flags = flags;
+    }
+
+    private Condition(Parcel source) {
+        id = Uri.CREATOR.createFromParcel(source);
+        caption = source.readString();
+        state = source.readInt() == 1;
+        flags = source.readInt();
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeParcelable(id, 0);
+        dest.writeString(caption);
+        dest.writeInt(state ? 1 : 0);
+        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(state)
+            .append(",flags=").append(flags)
+            .append(']').toString();
+    }
+
+    @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 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..8777e50
--- /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 Condition[] queryConditions(int relevance);
+    abstract public Condition[] getConditions(Uri[] conditionIds);
+    abstract public boolean subscribe(Uri conditionId);
+    abstract public boolean unsubscribe(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 (!isBound()) return;
+        try {
+            getNotificationInterface().notifyCondition(mProvider, condition);
+        } 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 Condition[] queryConditions(int relevance) {
+            try {
+                return mService.queryConditions(relevance);
+            } catch (Throwable t) {
+                Log.w(TAG, "Error running queryConditions", t);
+                return null;
+            }
+        }
+
+        @Override
+        public Condition[] getConditions(Uri[] conditionIds) {
+            try {
+                return mService.getConditions(conditionIds);
+            } catch (Throwable t) {
+                Log.w(TAG, "Error running getConditions", t);
+                return null;
+            }
+        }
+
+        @Override
+        public boolean subscribe(Uri conditionId) {
+            try {
+                return mService.subscribe(conditionId);
+            } catch (Throwable t) {
+                Log.w(TAG, "Error running subscribe", t);
+                return false;
+            }
+        }
+
+        @Override
+        public boolean unsubscribe(Uri conditionId) {
+            try {
+                return mService.unsubscribe(conditionId);
+            } catch (Throwable t) {
+                Log.w(TAG, "Error running unsubscribe", t);
+                return false;
+            }
+        }
+    }
+}
diff --git a/core/java/android/service/notification/IConditionProvider.aidl b/core/java/android/service/notification/IConditionProvider.aidl
new file mode 100644
index 0000000..cb582da
--- /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 */
+interface IConditionProvider {
+    Condition[] queryConditions(int relevance);
+    Condition[] getConditions(in Uri[] conditionIds);
+    boolean subscribe(in Uri conditionId);
+    boolean unsubscribe(in Uri conditionId);
+}
\ No newline at end of file
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/VoiceInteractionService.java b/core/java/android/service/voice/VoiceInteractionService.java
new file mode 100644
index 0000000..ed93b74
--- /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.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, VoiceInteractionSession session) {
+        try {
+            int res = mSystemService.startVoiceActivity(intent,
+                    intent.resolveType(getContentResolver()),
+                    mInterface, session.mSession, session.mInteractor);
+            Instrumentation.checkStartActivityResult(res, intent);
+        } 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/VoiceInteractionSession.java b/core/java/android/service/voice/VoiceInteractionSession.java
new file mode 100644
index 0000000..59544be
--- /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(Bundle result) {
+            try {
+                if (DEBUG) Log.d(TAG, "sendCommandResult: req=" + mInterface
+                        + " result=" + result);
+                mCallback.deliverCommandResult(mInterface, 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/view/View.java b/core/java/android/view/View.java
index 84d1328..c8e229a 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,32 @@
         } 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;
+                mOutline.markInvalid();
             }
-            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);
+            }
         }
     }
 
@@ -14893,11 +14869,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 +15271,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 +15279,10 @@
 
             invalidate(dirty.left + scrollX, dirty.top + scrollY,
                     dirty.right + scrollX, dirty.bottom + scrollY);
+
+            if (drawable == mBackground) {
+                queryOutlineFromBackgroundIfUndefined();
+            }
         }
     }
 
diff --git a/core/java/android/webkit/ClientCertRequest.java b/core/java/android/webkit/ClientCertRequest.java
new file mode 100644
index 0000000..8951786
--- /dev/null
+++ b/core/java/android/webkit/ClientCertRequest.java
@@ -0,0 +1,80 @@
+/*
+ * 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.webkit;
+
+import java.security.Principal;
+import java.security.PrivateKey;
+import java.security.cert.X509Certificate;
+
+/**
+ * ClientCertRequest: The user receives an instance of this class as
+ * a parameter of {@link WebViewClient#onReceivedClientCertRequest}.
+ * The request includes the parameters to choose the client certificate,
+ * such as the host name and the port number requesting the cert, the acceptable
+ * key types and the principals.
+ *
+ * The user should call one of the interface methods to indicate how to deal
+ * with the client certificate request. All methods should be called on
+ * UI thread.
+ *
+ * WebView caches the {@link #proceed} and {@link #cancel} responses in memory
+ * and uses them to handle future client certificate requests for the same
+ * host/port pair. The user can clear the cached data using
+ * {@link WebView#clearClientCertPreferences}.
+ *
+ * TODO(sgurun) unhide
+ * @hide
+ */
+public interface ClientCertRequest {
+    /**
+     * Returns the acceptable types of asymmetric keys (can be null).
+     */
+    public String[] getKeyTypes();
+
+    /**
+     * Returns the acceptable certificate issuers for the certificate
+     *            matching the private key (can be null).
+     */
+    public Principal[] getPrincipals();
+
+    /**
+     * Returns the host name of the server requesting the certificate.
+     */
+    public String getHost();
+
+    /**
+     * Returns the port number of the server requesting the certificate.
+     */
+    public int getPort();
+
+    /**
+     * Proceed with the specified private key and client certificate chain.
+     * Remember the user's positive choice and use it for future requests.
+     */
+    public void proceed(PrivateKey privateKey, X509Certificate[] chain);
+
+    /**
+     * Ignore the request for now. Do not remember user's choice.
+     */
+    public void ignore();
+
+    /**
+     * Cancel this request. Remember the user's choice and use it for
+     * future requests.
+     */
+    public void cancel();
+}
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index d2e7324..c914e52 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -485,7 +485,7 @@
      * @param privateBrowsing whether this WebView will be initialized in
      *                        private mode
      *
-     * @deprecated Private browsing is no longer supported directly via 
+     * @deprecated Private browsing is no longer supported directly via
      * WebView and will be removed in a future release. Prefer using
      * {@link WebSettings}, {@link WebViewDatabase}, {@link CookieManager}
      * and {@link WebStorage} for fine-grained control of privacy data.
@@ -1476,6 +1476,24 @@
     }
 
     /**
+     * Clears the client certificate preferences table stored in response
+     * to proceeding/cancelling client cert requests. Note that webview
+     * automatically clears these preferences when it receives a
+     * {@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
+     */
+    public void clearClientCertPreferences(ValueCallback<Void> resultCallback) {
+        checkThread();
+        if (DebugFlags.TRACE_API) Log.d(LOGTAG, "clearClientCertPreferences");
+        mProvider.clearClientCertPreferences(resultCallback);
+    }
+
+    /**
      * Gets the WebBackForwardList for this WebView. This contains the
      * back/forward list for use in querying each item in the history stack.
      * This is a copy of the private WebBackForwardList so it contains only a
diff --git a/core/java/android/webkit/WebViewClient.java b/core/java/android/webkit/WebViewClient.java
index e8974c6..688c251 100644
--- a/core/java/android/webkit/WebViewClient.java
+++ b/core/java/android/webkit/WebViewClient.java
@@ -22,6 +22,8 @@
 import android.view.KeyEvent;
 import android.view.ViewRootImpl;
 
+import java.security.Principal;
+
 public class WebViewClient {
 
     /**
@@ -205,6 +207,30 @@
     }
 
     /**
+     * Notify the host application to handle a SSL client certificate
+     * request. The host application is responsible for showing the UI
+     * if desired and providing the keys. There are three ways to
+     * respond: proceed(), cancel() or ignore(). Webview remembers the
+     * response if proceed() or cancel() is called and does not
+     * call onReceivedClientCertRequest() again for the same host and port
+     * pair. Webview does not remember the response if ignore() is called.
+     *
+     * This method is called on the UI thread. During the callback, the
+     * connection is suspended.
+     *
+     * The default behavior is to cancel, returning no client certificate.
+     *
+     * @param view The WebView that is initiating the callback
+     * @param request An instance of a {@link ClientCertRequest}
+     *
+     * TODO(sgurun) unhide
+     * @hide
+     */
+    public void onReceivedClientCertRequest(WebView view, ClientCertRequest request) {
+        request.cancel();
+    }
+
+    /**
      * Notifies the host application that the WebView received an HTTP
      * authentication request. The host application can use the supplied
      * {@link HttpAuthHandler} to set the WebView's response to the request.
diff --git a/core/java/android/webkit/WebViewProvider.java b/core/java/android/webkit/WebViewProvider.java
index 5081ff5..efa5497 100644
--- a/core/java/android/webkit/WebViewProvider.java
+++ b/core/java/android/webkit/WebViewProvider.java
@@ -198,6 +198,8 @@
 
     public void clearSslPreferences();
 
+    public void clearClientCertPreferences(ValueCallback<Void> resultCallback);
+
     public WebBackForwardList copyBackForwardList();
 
     public void setFindListener(WebView.FindListener listener);
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..e3c0728
--- /dev/null
+++ b/core/java/com/android/internal/app/IVoiceInteractionManagerService.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 com.android.internal.app;
+
+import android.content.Intent;
+
+import com.android.internal.app.IVoiceInteractor;
+import android.service.voice.IVoiceInteractionService;
+import android.service.voice.IVoiceInteractionSession;
+
+interface IVoiceInteractionManagerService {
+    int startVoiceActivity(in Intent intent, String resolvedType, IVoiceInteractionService service,
+            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..f5392e9
--- /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, 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_util_Process.cpp b/core/jni/android_util_Process.cpp
index a4efed7..01f4d3a 100644
--- a/core/jni/android_util_Process.cpp
+++ b/core/jni/android_util_Process.cpp
@@ -30,15 +30,16 @@
 #include "android_util_Binder.h"
 #include "JNIHelp.h"
 
-#include <sys/errno.h>
-#include <sys/resource.h>
-#include <sys/types.h>
-#include <sys/stat.h>
 #include <dirent.h>
 #include <fcntl.h>
 #include <grp.h>
+#include <inttypes.h>
 #include <pwd.h>
 #include <signal.h>
+#include <sys/errno.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
+#include <sys/types.h>
 #include <unistd.h>
 
 #define POLICY_DEBUG 0
@@ -159,7 +160,7 @@
 
 void android_os_Process_setThreadGroup(JNIEnv* env, jobject clazz, int tid, jint grp)
 {
-    ALOGV("%s tid=%d grp=%d", __func__, tid, grp);
+    ALOGV("%s tid=%d grp=%" PRId32, __func__, tid, grp);
     SchedPolicy sp = (SchedPolicy) grp;
     int res = set_sched_policy(tid, sp);
     if (res != NO_ERROR) {
@@ -169,7 +170,7 @@
 
 void android_os_Process_setProcessGroup(JNIEnv* env, jobject clazz, int pid, jint grp)
 {
-    ALOGV("%s pid=%d grp=%d", __func__, pid, grp);
+    ALOGV("%s pid=%d grp=%" PRId32, __func__, pid, grp);
     DIR *d;
     FILE *fp;
     char proc_path[255];
@@ -322,7 +323,7 @@
         }
     }
 
-    //ALOGI("Setting priority of %d: %d, getpriority returns %d\n",
+    //ALOGI("Setting priority of %" PRId32 ": %" PRId32 ", getpriority returns %d\n",
     //     pid, pri, getpriority(PRIO_PROCESS, pid));
 }
 
@@ -340,7 +341,7 @@
     if (errno != 0) {
         signalExceptionForPriorityError(env, errno);
     }
-    //ALOGI("Returning priority of %d: %d\n", pid, pri);
+    //ALOGI("Returning priority of %" PRId32 ": %" PRId32 "\n", pid, pri);
     return pri;
 }
 
@@ -362,7 +363,7 @@
 
     int fd = open(text, O_WRONLY);
     if (fd >= 0) {
-        sprintf(text, "%d", pid);
+        sprintf(text, "%" PRId32, pid);
         write(fd, text, strlen(text));
         close(fd);
     }
@@ -403,7 +404,7 @@
 
 static int pid_compare(const void* v1, const void* v2)
 {
-    //ALOGI("Compare %d vs %d\n", *((const jint*)v1), *((const jint*)v2));
+    //ALOGI("Compare %" PRId32 " vs %" PRId32 "\n", *((const jint*)v1), *((const jint*)v2));
     return *((const jint*)v1) - *((const jint*)v2);
 }
 
@@ -517,7 +518,7 @@
         return;
     }
 
-    //ALOGI("Clearing %d sizes", count);
+    //ALOGI("Clearing %" PRId32 " sizes", count);
     for (i=0; i<count; i++) {
         sizesArray[i] = 0;
     }
@@ -556,7 +557,7 @@
                     }
                     char* end;
                     sizesArray[i] = strtoll(num, &end, 10);
-                    //ALOGI("Field %s = %d", field.string(), sizesArray[i]);
+                    //ALOGI("Field %s = %" PRId64, field.string(), sizesArray[i]);
                     foundCount++;
                     break;
                 }
@@ -758,7 +759,7 @@
             }
         }
 
-        //ALOGI("Field %d: %d-%d dest=%d mode=0x%x\n", i, start, end, di, mode);
+        //ALOGI("Field %" PRId32 ": %" PRId32 "-%" PRId32 " dest=%" PRId32 " mode=0x%" PRIx32 "\n", i, start, end, di, mode);
 
         if ((mode&(PROC_OUT_FLOAT|PROC_OUT_LONG|PROC_OUT_STRING)) != 0) {
             char c = buffer[end];
@@ -857,7 +858,7 @@
 void android_os_Process_sendSignal(JNIEnv* env, jobject clazz, jint pid, jint sig)
 {
     if (pid > 0) {
-        ALOGI("Sending signal. PID: %d SIG: %d", pid, sig);
+        ALOGI("Sending signal. PID: %" PRId32 " SIG: %" PRId32, pid, sig);
         kill(pid, sig);
     }
 }
@@ -887,7 +888,7 @@
 {
     char filename[64];
 
-    snprintf(filename, sizeof(filename), "/proc/%d/smaps", pid);
+    snprintf(filename, sizeof(filename), "/proc/%" PRId32 "/smaps", pid);
 
     FILE * file = fopen(filename, "r");
     if (!file) {
@@ -899,7 +900,7 @@
     jlong pss = 0;
     while (fgets(line, sizeof(line), file)) {
         jlong v;
-        if (sscanf(line, "Pss: %lld kB", &v) == 1) {
+        if (sscanf(line, "Pss: %" SCNd64 " kB", &v) == 1) {
             pss += v;
         }
     }
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/values/attrs.xml b/core/res/res/values/attrs.xml
index 508a557..326485d 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -6267,13 +6267,21 @@
     </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="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/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/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/docs/html/guide/topics/data/backup.jd b/docs/html/guide/topics/data/backup.jd
index 4903852..f09ff9e 100644
--- a/docs/html/guide/topics/data/backup.jd
+++ b/docs/html/guide/topics/data/backup.jd
@@ -899,8 +899,9 @@
 {@code tools/} path:
 <pre class="no-pretty-print">adb shell bmgr enable true</pre>
       </li>
-      <li>If using a device, open the system <b>Settings</b>, select <b>Privacy</b>, then enable
-<b>Back up my data</b> and <b>Automatic restore</b>.
+      <li>If using a device, open the system <b>Settings</b>, select
+      <b>Backup & reset</b>, then enable
+      <b>Back up my data</b> and <b>Automatic restore</b>.</li>
     </ul>
   </li>
   <li>Open your application and initialize some data
diff --git a/graphics/java/android/graphics/Outline.java b/graphics/java/android/graphics/Outline.java
index 4ea7ec7..d3aac79 100644
--- a/graphics/java/android/graphics/Outline.java
+++ b/graphics/java/android/graphics/Outline.java
@@ -16,18 +16,19 @@
 
 package android.graphics;
 
+import android.graphics.drawable.Drawable;
 import android.view.View;
 
 /**
  * Defines an area of content.
  *
- * Can be used with a View or Drawable to drive the shape of shadows cast by a
+ * Can be used with a View, or computed by a Drawable, to drive the shape of shadows cast by a
  * View, and allowing Views to clip inner content.
  *
  * @see View#setOutline(Outline)
- * @see View#setClipToOutline(boolean)
+ * @see Drawable#getOutline(Outline)
  */
-public class Outline {
+public final class Outline {
     /** @hide */
     public Rect mRect;
 
@@ -44,22 +45,29 @@
     public Outline() {}
 
     /**
+     * Constructs an Outline with a copy of the data in src.
+     */
+    public Outline(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.
      */
     public void set(Outline src) {
@@ -81,9 +89,20 @@
 
     /**
      * 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(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.
      */
     public void setRoundRect(int left, int top, int right, int bottom, float radius) {
         if (mRect == null) mRect = new Rect();
@@ -93,9 +112,16 @@
     }
 
     /**
+     * Convenience for {@link #setRoundRect(int, int, int, int, float)}
+     * @param rect
+     * @param radius
+     */
+    public void setRoundRect(Rect rect, float radius) {
+        setRoundRect(rect.left, rect.top, rect.right, rect.bottom, radius);
+    }
+
+    /**
      * Sets the Constructs an Outline from a {@link android.graphics.Path#isConvex() convex path}.
-     *
-     * @hide
      */
     public void setConvexPath(Path convexPath) {
         if (!convexPath.isConvex()) {
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..708c8b0 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;
@@ -1428,42 +1432,39 @@
     }
 
     @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,
+                outline.setRoundRect(bounds.left, bounds.top,
                         bounds.right, bounds.bottom, rad);
-                return mOutline;
+                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/media/java/android/media/DngCreator.java b/media/java/android/media/DngCreator.java
new file mode 100644
index 0000000..b2a38ab
--- /dev/null
+++ b/media/java/android/media/DngCreator.java
@@ -0,0 +1,254 @@
+/*
+ * Copyright 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.graphics.Bitmap;
+import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.CaptureResult;
+import android.location.Location;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.ByteBuffer;
+
+/**
+ * The {@link DngCreator} class provides functions to write raw pixel data as a DNG file.
+ *
+ * <p>
+ * This class is designed to be used with the {@link android.graphics.ImageFormat#RAW_SENSOR}
+ * buffers available from {@link android.hardware.camera2.CameraDevice}, or with Bayer-type raw
+ * pixel data that is otherwise generated by an application.  The DNG metadata tags will be
+ * generated from a {@link android.hardware.camera2.CaptureResult} object or set directly.
+ * </p>
+ *
+ * <p>
+ * The DNG file format is a cross-platform file format that is used to store pixel data from
+ * camera sensors with minimal pre-processing applied.  DNG files allow for pixel data to be
+ * defined in a user-defined colorspace, and have associated metadata that allow for this
+ * pixel data to be converted to the standard CIE XYZ colorspace during post-processing.
+ * </p>
+ *
+ * <p>
+ * For more information on the DNG file format and associated metadata, please refer to the
+ * <a href=
+ * "https://wwwimages2.adobe.com/content/dam/Adobe/en/products/photoshop/pdfs/dng_spec_1.4.0.0.pdf">
+ * Adobe DNG 1.4.0.0 specification</a>.
+ * </p>
+ */
+public final class DngCreator {
+
+    /**
+     * Create a new DNG object.
+     *
+     * <p>
+     * It is not necessary to call any set methods to write a well-formatted DNG file.
+     * </p>
+     * <p>
+     * DNG metadata tags will be generated from the corresponding parameters in the
+     * {@link android.hardware.camera2.CaptureResult} object.  This removes or overrides
+     * all previous tags set.
+     * </p>
+     *
+     * @param characteristics an object containing the static
+     *          {@link android.hardware.camera2.CameraCharacteristics}.
+     * @param metadata a metadata object to generate tags from.
+     */
+    public DngCreator(CameraCharacteristics characteristics, CaptureResult metadata) {/*TODO*/}
+
+    /**
+     * Set the orientation value to write.
+     *
+     * <p>
+     * This will be written as the TIFF "Orientation" tag {@code (0x0112)}.
+     * Calling this will override any prior settings for this tag.
+     * </p>
+     *
+     * @param orientation the orientation value to set, one of:
+     *                    <ul>
+     *                      <li>{@link android.media.ExifInterface#ORIENTATION_NORMAL}</li>
+     *                      <li>{@link android.media.ExifInterface#ORIENTATION_FLIP_HORIZONTAL}</li>
+     *                      <li>{@link android.media.ExifInterface#ORIENTATION_ROTATE_180}</li>
+     *                      <li>{@link android.media.ExifInterface#ORIENTATION_FLIP_VERTICAL}</li>
+     *                      <li>{@link android.media.ExifInterface#ORIENTATION_TRANSPOSE}</li>
+     *                      <li>{@link android.media.ExifInterface#ORIENTATION_ROTATE_90}</li>
+     *                      <li>{@link android.media.ExifInterface#ORIENTATION_TRANSVERSE}</li>
+     *                      <li>{@link android.media.ExifInterface#ORIENTATION_ROTATE_270}</li>
+     *                    </ul>
+     * @return this {@link #DngCreator} object.
+     */
+    public DngCreator setOrientation(int orientation) {
+        return this;
+    }
+
+    /**
+     * Set the thumbnail image.
+     *
+     * <p>
+     * Pixel data will be converted to a Baseline TIFF RGB image, with 8 bits per color channel.
+     * The alpha channel will be discarded.
+     * </p>
+     *
+     * <p>
+     * The given bitmap should not be altered while this object is in use.
+     * </p>
+     *
+     * @param pixels a {@link android.graphics.Bitmap} of pixel data.
+     * @return this {@link #DngCreator} object.
+     */
+    public DngCreator setThumbnail(Bitmap pixels) {
+        return this;
+    }
+
+    /**
+     * Set the thumbnail image.
+     *
+     * <p>
+     * Pixel data is interpreted as a {@link android.graphics.ImageFormat#YUV_420_888} image.
+     * </p>
+     *
+     * <p>
+     * The given image should not be altered while this object is in use.
+     * </p>
+     *
+     * @param pixels an {@link android.media.Image} object with the format
+     *               {@link android.graphics.ImageFormat#YUV_420_888}.
+     * @return this {@link #DngCreator} object.
+     */
+    public DngCreator setThumbnail(Image pixels) {
+        return this;
+    }
+
+
+    /**
+     * Set image location metadata.
+     *
+     * <p>
+     * The given location object must contain at least a valid time, latitude, and longitude
+     * (equivalent to the values returned by {@link android.location.Location#getTime()},
+     * {@link android.location.Location#getLatitude()}, and
+     * {@link android.location.Location#getLongitude()} methods).
+     * </p>
+     *
+     * @param location an {@link android.location.Location} object to set.
+     * @return this {@link #DngCreator} object.
+     *
+     * @throws java.lang.IllegalArgumentException if the given location object doesn't
+     *          contain enough information to set location metadata.
+     */
+    public DngCreator setLocation(Location location) { return this; }
+
+    /**
+     * Set the user description string to write.
+     *
+     * <p>
+     * This is equivalent to setting the TIFF "ImageDescription" tag {@code (0x010E)}.
+     * </p>
+     *
+     * @param description the user description string.
+     * @return this {@link #DngCreator} object.
+     */
+    public DngCreator setDescription(String description) {
+        return this;
+    }
+
+    /**
+     * Write the {@link android.graphics.ImageFormat#RAW_SENSOR} pixel data to a DNG file with
+     * the currently configured metadata.
+     *
+     * <p>
+     * Raw pixel data must have 16 bits per pixel, and the input must contain at least
+     * {@code offset + 2 * (stride * (height - 1) + width * height)} bytes.  The width and height of
+     * the input are taken from the width and height set in the {@link DngCreator} metadata tags,
+     * and will typically be equal to the width and height of
+     * {@link android.hardware.camera2.CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE}.
+     * If insufficient metadata is set to write a well-formatted DNG file, and
+     * {@link java.lang.IllegalStateException} will be thrown.
+     * </p>
+     *
+     * <p>
+     * When reading from the pixel input, {@code stride} pixels will be skipped
+     * after each row (excluding the last).
+     * </p>
+     *
+     * @param dngOutput an {@link java.io.OutputStream} to write the DNG file to.
+     * @param pixels an {@link java.io.InputStream} of pixel data to write.
+     * @param stride the stride of the raw image in pixels.
+     * @param offset the offset of the raw image in bytes.  This indicates how many bytes will
+     *               be skipped in the input before any pixel data is read.
+     *
+     * @throws IOException if an error was encountered in the input or output stream.
+     * @throws java.lang.IllegalStateException if not enough metadata information has been
+     *          set to write a well-formatted DNG file.
+     */
+    public void writeInputStream(OutputStream dngOutput, InputStream pixels, int stride,
+                                 long offset) throws IOException {
+        /*TODO*/
+    }
+
+    /**
+     * Write the {@link android.graphics.ImageFormat#RAW_SENSOR} pixel data to a DNG file with
+     * the currently configured metadata.
+     *
+     * <p>
+     * Raw pixel data must have 16 bits per pixel, and the input must contain at least
+     * {@code offset + 2 * (stride * (height - 1) + width * height)} bytes.  The width and height of
+     * the input are taken from the width and height set in the {@link DngCreator} metadata tags,
+     * and will typically be equal to the width and height of
+     * {@link android.hardware.camera2.CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE}.
+     * If insufficient metadata is set to write a well-formatted DNG file, and
+     * {@link java.lang.IllegalStateException} will be thrown.
+     * </p>
+     *
+     * <p>
+     * When reading from the pixel input, {@code stride} pixels will be skipped
+     * after each row (excluding the last).
+     * </p>
+     *
+     * @param dngOutput an {@link java.io.OutputStream} to write the DNG file to.
+     * @param pixels an {@link java.nio.ByteBuffer} of pixel data to write.
+     * @param stride the stride of the raw image in pixels.
+     * @param offset the offset of the raw image in bytes.  This indicates how many bytes will
+     *               be skipped in the input before any pixel data is read.
+     *
+     * @throws IOException if an error was encountered in the input or output stream.
+     * @throws java.lang.IllegalStateException if not enough metadata information has been
+     *          set to write a well-formatted DNG file.
+     */
+    public void writeByteBuffer(OutputStream dngOutput, ByteBuffer pixels, int stride,
+                                long offset) throws IOException {/*TODO*/}
+
+    /**
+     * Write the pixel data to a DNG file with the currently configured metadata.
+     *
+     * <p>
+     * For this method to succeed, the {@link android.media.Image} input must contain
+     * {@link android.graphics.ImageFormat#RAW_SENSOR} pixel data, otherwise an
+     * {@link java.lang.IllegalArgumentException} will be thrown.
+     * </p>
+     *
+     * @param dngOutput an {@link java.io.OutputStream} to write the DNG file to.
+     * @param pixels an {@link android.media.Image} to write.
+     *
+     * @throws java.io.IOException if an error was encountered in the output stream.
+     * @throws java.lang.IllegalArgumentException if an image with an unsupported format was used.
+     * @throws java.lang.IllegalStateException if not enough metadata information has been
+     *          set to write a well-formatted DNG file.
+     */
+    public void writeImage(OutputStream dngOutput, Image pixels) throws IOException {/*TODO*/}
+
+}
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/packages/Keyguard/src/com/android/keyguard/KeyguardMessageArea.java b/packages/Keyguard/src/com/android/keyguard/KeyguardMessageArea.java
index 2e4dbdf..ede23ef 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardMessageArea.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardMessageArea.java
@@ -27,11 +27,10 @@
 import android.os.SystemClock;
 import android.text.TextUtils;
 import android.util.AttributeSet;
+import android.util.MutableInt;
 import android.view.View;
 import android.widget.TextView;
 
-import libcore.util.MutableInt;
-
 import java.lang.ref.WeakReference;
 
 import com.android.internal.widget.LockPatternUtils;
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/src/com/android/systemui/recent/Recents.java b/packages/SystemUI/src/com/android/systemui/recent/Recents.java
index 21c2926..ae18aa8 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/Recents.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/Recents.java
@@ -50,7 +50,8 @@
 
     @Override
     public void start() {
-        mUseAlternateRecents = SystemProperties.getBoolean("persist.recents.use_alternate", false);
+        Configuration config = mContext.getResources().getConfiguration();
+        mUseAlternateRecents = (config.smallestScreenWidthDp < 600);
         if (mUseAlternateRecents) {
             if (mAlternateRecents == null) {
                 mAlternateRecents = new AlternateRecentsComponent(mContext);
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 f26fa79..a274de6d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -3002,14 +3002,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/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/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index 2f56e62..87b1d32 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -50,6 +50,7 @@
 import android.os.UserHandle;
 import android.util.AtomicFile;
 import android.util.AttributeSet;
+import android.util.MutableInt;
 import android.util.Pair;
 import android.util.Slog;
 import android.util.SparseArray;
@@ -86,8 +87,6 @@
 import java.util.Map.Entry;
 import java.util.Set;
 
-import libcore.util.MutableInt;
-
 class AppWidgetServiceImpl {
 
     private static final String KEYGUARD_HOST_PACKAGE = "com.android.keyguard";
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index ed007e9..75bf5df 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -28,17 +28,20 @@
 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.os.BackgroundThread;
@@ -56,6 +59,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 +337,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 +406,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 +886,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 +1130,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 +1711,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 +1766,14 @@
                 }
                 break;
             }
+            case SYSTEM_USER_START_MSG: {
+                mSystemServiceManager.startUser(msg.arg1);
+                break;
+            }
+            case SYSTEM_USER_CURRENT_MSG: {
+                mSystemServiceManager.switchUser(msg.arg1);
+                break;
+            }
             }
         }
     };
@@ -2059,6 +2082,10 @@
         Watchdog.getInstance().addThread(mHandler);
     }
 
+    public void setSystemServiceManager(SystemServiceManager mgr) {
+        mSystemServiceManager = mgr;
+    }
+
     private void start() {
         mProcessCpuThread.start();
 
@@ -2254,6 +2281,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 +2294,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 +3028,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 +3159,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 +3185,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 +3200,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 +3215,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 +3253,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 +3370,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 +3393,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 +3429,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();
@@ -8509,11 +8576,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 +8659,7 @@
     }
 
     private void comeOutOfSleepIfNeededLocked() {
-        if (!mWentToSleep && !mLockScreenShown) {
+        if ((!mWentToSleep && !mLockScreenShown) || mRunningVoice) {
             if (mSleeping) {
                 mSleeping = false;
                 mStackSupervisor.comeOutOfSleepIfNeededLocked();
@@ -8592,6 +8675,13 @@
         }
     }
 
+    void startRunningVoiceLocked() {
+        if (!mRunningVoice) {
+            mRunningVoice = true;
+            comeOutOfSleepIfNeededLocked();
+        }
+    }
+
     private void updateEventDispatchingLocked() {
         mWindowManager.setEventDispatching(mBooted && !mWentToSleep && !mShuttingDown);
     }
@@ -9298,7 +9388,7 @@
                     proc.notCachedSinceIdle = true;
                     proc.initialIdlePss = 0;
                     proc.nextPssTime = ProcessList.computeNextPssTime(proc.curProcState, true,
-                            mSleeping, now);
+                            isSleeping(), now);
                 }
             }
 
@@ -9597,6 +9687,8 @@
 
         if (goingCallback != null) goingCallback.run();
         
+        mSystemServiceManager.startUser(mCurrentUserId);
+
         synchronized (this) {
             if (mFactoryTest != FactoryTest.FACTORY_TEST_LOW_LEVEL) {
                 try {
@@ -9653,6 +9745,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 +11246,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 +15367,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 +15404,7 @@
             }
         }
         return !processingBroadcasts
-                && (mSleeping || mStackSupervisor.allResumedActivitiesIdle());
+                && (isSleeping() || mStackSupervisor.allResumedActivitiesIdle());
     }
     
     /**
@@ -15585,7 +15679,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 +15689,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 +16026,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 +16569,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 +16932,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 +16982,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 9f57728..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");
@@ -735,7 +742,9 @@
         int w = mThumbnailWidth;
         int h = mThumbnailHeight;
         if (w < 0) {
-            if (SystemProperties.getBoolean("persist.recents.use_alternate", false)) {
+            Configuration config = res.getConfiguration();
+            boolean useAlternateRecents = (config.smallestScreenWidthDp < 600);
+            if (useAlternateRecents) {
                 mThumbnailWidth = w =
                    res.getDimensionPixelSize(com.android.internal.R.dimen.recents_thumbnail_width);
                 mThumbnailHeight = h =
@@ -1501,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");
@@ -2032,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
@@ -2329,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);
                     }
@@ -2708,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;
@@ -2737,9 +2746,7 @@
         if (mPausingActivity == r) {
             mPausingActivity = null;
         }
-        if (mService.mFocusedActivity == r) {
-            mService.mFocusedActivity = null;
-        }
+        mService.clearFocusedActivity(r);
 
         r.configDestroy = false;
         r.frozenBeforeDestroy = false;
@@ -3721,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);
@@ -3734,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..274e011 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);
         }
 
diff --git a/services/core/java/com/android/server/am/NativeCrashListener.java b/services/core/java/com/android/server/am/NativeCrashListener.java
index 443218c..d42d415 100644
--- a/services/core/java/com/android/server/am/NativeCrashListener.java
+++ b/services/core/java/com/android/server/am/NativeCrashListener.java
@@ -17,14 +17,13 @@
 package com.android.server.am;
 
 import android.app.ApplicationErrorReport.CrashInfo;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.system.StructTimeval;
+import android.system.StructUcred;
 import android.util.Slog;
 
-import libcore.io.ErrnoException;
-import libcore.io.Libcore;
-import libcore.io.StructTimeval;
-import libcore.io.StructUcred;
-
-import static libcore.io.OsConstants.*;
+import static android.system.OsConstants.*;
 
 import java.io.ByteArrayOutputStream;
 import java.io.File;
@@ -77,7 +76,7 @@
             try {
                 CrashInfo ci = new CrashInfo();
                 ci.exceptionClassName = "Native crash";
-                ci.exceptionMessage = Libcore.os.strsignal(mSignal);
+                ci.exceptionMessage = Os.strsignal(mSignal);
                 ci.throwFileName = "unknown";
                 ci.throwClassName = "unknown";
                 ci.throwMethodName = "unknown";
@@ -117,22 +116,22 @@
         }
 
         try {
-            FileDescriptor serverFd = Libcore.os.socket(AF_UNIX, SOCK_STREAM, 0);
+            FileDescriptor serverFd = Os.socket(AF_UNIX, SOCK_STREAM, 0);
             final InetUnixAddress sockAddr = new InetUnixAddress(DEBUGGERD_SOCKET_PATH);
-            Libcore.os.bind(serverFd, sockAddr, 0);
-            Libcore.os.listen(serverFd, 1);
+            Os.bind(serverFd, sockAddr, 0);
+            Os.listen(serverFd, 1);
 
             while (true) {
                 InetSocketAddress peer = new InetSocketAddress();
                 FileDescriptor peerFd = null;
                 try {
                     if (MORE_DEBUG) Slog.v(TAG, "Waiting for debuggerd connection");
-                    peerFd = Libcore.os.accept(serverFd, peer);
+                    peerFd = Os.accept(serverFd, peer);
                     if (MORE_DEBUG) Slog.v(TAG, "Got debuggerd socket " + peerFd);
                     if (peerFd != null) {
                         // Only the superuser is allowed to talk to us over this socket
                         StructUcred credentials =
-                                Libcore.os.getsockoptUcred(peerFd, SOL_SOCKET, SO_PEERCRED);
+                                Os.getsockoptUcred(peerFd, SOL_SOCKET, SO_PEERCRED);
                         if (credentials.uid == 0) {
                             // the reporting thread may take responsibility for
                             // acking the debugger; make sure we play along.
@@ -146,7 +145,7 @@
                     // byte written is irrelevant.
                     if (peerFd != null) {
                         try {
-                            Libcore.os.write(peerFd, ackSignal, 0, 1);
+                            Os.write(peerFd, ackSignal, 0, 1);
                         } catch (Exception e) {
                             /* we don't care about failures here */
                             if (MORE_DEBUG) {
@@ -154,7 +153,7 @@
                             }
                         }
                         try {
-                            Libcore.os.close(peerFd);
+                            Os.close(peerFd);
                         } catch (ErrnoException e) {
                             if (MORE_DEBUG) {
                                 Slog.d(TAG, "Exception closing socket: " + e.getMessage());
@@ -182,7 +181,7 @@
             throws ErrnoException, InterruptedIOException {
         int totalRead = 0;
         while (numBytes > 0) {
-            int n = Libcore.os.read(fd, buffer, offset + totalRead, numBytes);
+            int n = Os.read(fd, buffer, offset + totalRead, numBytes);
             if (n <= 0) {
                 if (DEBUG) {
                     Slog.w(TAG, "Needed " + numBytes + " but saw " + n);
@@ -203,8 +202,8 @@
 
         try {
             StructTimeval timeout = StructTimeval.fromMillis(SOCKET_TIMEOUT_MILLIS);
-            Libcore.os.setsockoptTimeval(fd, SOL_SOCKET, SO_RCVTIMEO, timeout);
-            Libcore.os.setsockoptTimeval(fd, SOL_SOCKET, SO_SNDTIMEO, timeout);
+            Os.setsockoptTimeval(fd, SOL_SOCKET, SO_RCVTIMEO, timeout);
+            Os.setsockoptTimeval(fd, SOL_SOCKET, SO_SNDTIMEO, timeout);
 
             // first, the pid and signal number
             int headerBytes = readExactly(fd, buf, 0, 8);
@@ -238,7 +237,7 @@
                     int bytes;
                     do {
                         // get some data
-                        bytes = Libcore.os.read(fd, buf, 0, buf.length);
+                        bytes = Os.read(fd, buf, 0, buf.length);
                         if (bytes > 0) {
                             if (MORE_DEBUG) {
                                 String s = new String(buf, 0, bytes, "UTF-8");
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/notification/ConditionProviders.java b/services/core/java/com/android/server/notification/ConditionProviders.java
new file mode 100644
index 0000000..a282270
--- /dev/null
+++ b/services/core/java/com/android/server/notification/ConditionProviders.java
@@ -0,0 +1,58 @@
+/**
+ * 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.os.Handler;
+import android.os.IBinder;
+import android.os.IInterface;
+import android.provider.Settings;
+import android.service.notification.IConditionProvider;
+import android.service.notification.ConditionProviderService;
+import android.util.Slog;
+
+import com.android.internal.R;
+
+public class ConditionProviders extends ManagedServices {
+
+    public ConditionProviders(Context context, Handler handler,
+            Object mutex, UserProfiles userProfiles) {
+        super(context, handler, mutex, userProfiles);
+    }
+
+    @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
+    protected IInterface asInterface(IBinder binder) {
+        return IConditionProvider.Stub.asInterface(binder);
+    }
+
+    @Override
+    protected void onServiceAdded(IInterface service) {
+        Slog.d(TAG, "onServiceAdded " + service);
+    }
+}
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..81b28e8
--- /dev/null
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -0,0 +1,594 @@
+/**
+ * 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.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;
+    private 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);
+
+    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) {
+        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);
+
+            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);
+                    mServices.remove(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) {
+                    mServices.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, 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) {
+        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) {
+                    serviceInfo = mServices.remove(i);
+                }
+            }
+        }
+        return serviceInfo;
+    }
+
+    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;
+        }
+
+        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() {
+            // 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..5a1f9b2 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,18 @@
 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.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 +69,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 +81,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 +105,6 @@
 import java.util.Arrays;
 import java.util.HashSet;
 import java.util.Iterator;
-import java.util.List;
 import java.util.NoSuchElementException;
 
 /** {@hide} */
@@ -191,8 +194,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();
 
@@ -745,6 +749,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.
@@ -845,18 +850,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, mNotificationList, mUserProfiles);
         mStatusBar = getLocalService(StatusBarManagerInternal.class);
         mStatusBar.setNotificationDelegate(mNotificationDelegate);
 
@@ -972,6 +968,7 @@
             // bind to listener services.
             mSettingsObserver.observe();
             mListeners.onBootPhaseAppsCanStart();
+            mConditionProviders.onBootPhaseAppsCanStart();
         }
     }
 
@@ -1005,8 +1002,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 +1093,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 +1105,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 +1119,7 @@
 
         @Override
         public void setNotificationsEnabledForPackage(String pkg, int uid, boolean enabled) {
-            NotificationUtil.checkCallerIsSystem();
+            checkCallerIsSystem();
 
             setNotificationsEnabledForPackageImpl(pkg, uid, enabled);
         }
@@ -1133,7 +1129,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 +1197,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 +1206,7 @@
          */
         @Override
         public void unregisterListener(INotificationListener listener, int userid) {
-            mListeners.unregisterListener(listener, userid);
+            mListeners.unregisterService(listener, userid);
         }
 
         /**
@@ -1227,8 +1223,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 +1232,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 +1250,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 +1273,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 +1299,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 +1314,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 +1330,23 @@
 
         @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 notifyCondition(IConditionProvider provider, Condition condition) {
+            // TODO check token
+            mZenModeHelper.notifyCondition(condition);
+        }
+
+        @Override
         protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
             if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
                     != PackageManager.PERMISSION_GRANTED) {
@@ -1362,12 +1362,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());
                 }
             }
@@ -1379,6 +1379,7 @@
         pw.println("Current Notification Manager state:");
 
         mListeners.dump(pw);
+        mConditionProviders.dump(pw);
 
         int N;
 
@@ -1455,9 +1456,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 +2028,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 +2099,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 +2141,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 +2234,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..b00beb6 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -33,6 +33,7 @@
 import android.os.Handler;
 import android.os.IBinder;
 import android.provider.Settings.Global;
+import android.service.notification.Condition;
 import android.service.notification.ZenModeConfig;
 import android.util.Slog;
 
@@ -201,6 +202,10 @@
         return true;
     }
 
+    public void notifyCondition(Condition condition) {
+        Slog.d(TAG, "notifyCondition " + condition);
+    }
+
     private boolean isCall(String pkg, Notification n) {
         return CALL_PACKAGES.contains(pkg);
     }
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 2756a1c..ad07084 100755
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -104,7 +104,6 @@
 import android.os.FileObserver;
 import android.os.FileUtils;
 import android.os.Handler;
-import android.os.HandlerThread;
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
@@ -276,8 +275,6 @@
     final PackageHandler mHandler;
 
     final int mSdkVersion = Build.VERSION.SDK_INT;
-    final String mSdkCodename = "REL".equals(Build.VERSION.CODENAME)
-            ? null : Build.VERSION.CODENAME;
 
     final Context mContext;
     final boolean mFactoryTest;
@@ -2163,6 +2160,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");
@@ -4835,7 +4850,6 @@
                     pkg.applicationInfo.nativeLibraryDir = pkgSetting.nativeLibraryPathString;
                 }
             }
-
             pkgSetting.uidError = uidError;
         }
 
@@ -5416,7 +5430,8 @@
         }
     }
 
-    private String calculateApkRoot(final File codePath) {
+    private String calculateApkRoot(final String codePathString) {
+        final File codePath = new File(codePathString);
         final File codeRoot;
         if (FileUtils.contains(Environment.getRootDirectory(), codePath)) {
             codeRoot = Environment.getRootDirectory();
@@ -5453,12 +5468,12 @@
             PackageSetting pkgSetting) {
         // "bundled" here means system-installed with no overriding update
         final boolean bundledApk = isSystemApp(pkg) && !isUpdatedSystemApp(pkg);
-        final String apkName = getApkName(pkgSetting.codePathString);
+        final String apkName = getApkName(pkg.applicationInfo.sourceDir);
         final File libDir;
         if (bundledApk) {
             // If "/system/lib64/apkname" exists, assume that is the per-package
             // native library directory to use; otherwise use "/system/lib/apkname".
-            String apkRoot = calculateApkRoot(pkgSetting.codePath);
+            String apkRoot = calculateApkRoot(pkg.applicationInfo.sourceDir);
             File lib64 = new File(apkRoot, LIB64_DIR_NAME);
             File packLib64 = new File(lib64, apkName);
             libDir = (packLib64.exists()) ? lib64 : new File(apkRoot, LIB_DIR_NAME);
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/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index f17b2f4..9039236 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -162,13 +162,10 @@
     private final Interpolator mThumbnailFadeoutInterpolator;
 
     private int mCurrentUserId = 0;
-    private boolean mUseAlternateThumbnailAnimation;
 
     AppTransition(Context context, Handler h) {
         mContext = context;
         mH = h;
-        mUseAlternateThumbnailAnimation =
-                SystemProperties.getBoolean("persist.anim.use_alt_thumbnail", false);
         mConfigShortAnimTime = context.getResources().getInteger(
                 com.android.internal.R.integer.config_shortAnimTime);
         mDecelerateInterpolator = AnimationUtils.loadInterpolator(context,
@@ -668,7 +665,7 @@
 
     Animation loadAnimation(WindowManager.LayoutParams lp, int transit, boolean enter,
                             int appWidth, int appHeight, int orientation,
-                            Rect containingFrame, Rect contentInsets) {
+                            Rect containingFrame, Rect contentInsets, Configuration configuration) {
         Animation a;
         if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_CUSTOM) {
             a = loadAnimation(mNextAppTransitionPackage, enter ?
@@ -689,7 +686,8 @@
                 mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_DOWN) {
             mNextAppTransitionScaleUp =
                     (mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP);
-            if (mUseAlternateThumbnailAnimation) {
+            boolean useAlternateThumbnailAnimation = (configuration.smallestScreenWidthDp < 600);
+            if (useAlternateThumbnailAnimation) {
                 a = createAlternateThumbnailEnterExitAnimationLocked(
                         getThumbnailTransitionState(enter), appWidth, appHeight, orientation,
                         transit, containingFrame, contentInsets);
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index b56377c..637beec 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -3190,7 +3190,7 @@
             }
 
             Animation a = mAppTransition.loadAnimation(lp, transit, enter, width, height,
-                    mCurConfiguration.orientation, containingFrame, contentInsets);
+                    mCurConfiguration.orientation, containingFrame, contentInsets, mCurConfiguration);
             if (a != null) {
                 if (DEBUG_ANIM) {
                     RuntimeException e = null;
@@ -8660,8 +8660,7 @@
                 wtoken.deferClearAllDrawn = false;
             }
 
-            boolean useAlternateThumbnailAnimation =
-                            SystemProperties.getBoolean("persist.anim.use_alt_thumbnail", false);
+            boolean useAlternateThumbnailAnimation = (mCurConfiguration.smallestScreenWidthDp < 600);
             AppWindowAnimator appAnimator =
                     topOpeningApp == null ? null : topOpeningApp.mAppAnimator;
             Bitmap nextAppTransitionThumbnail = mAppTransition.getNextAppTransitionThumbnail();
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..00d5468 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -61,6 +61,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 +115,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 =
@@ -290,6 +293,7 @@
         // Activity manager runs the show.
         mActivityManagerService = mSystemServiceManager.startService(
                 ActivityManagerService.Lifecycle.class).getService();
+        mActivityManagerService.setSystemServiceManager(mSystemServiceManager);
     }
 
     private void startCoreServices() {
@@ -826,6 +830,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 +932,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..9e2bcab
--- /dev/null
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -0,0 +1,209 @@
+/*
+ * 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.content.ComponentName;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.database.ContentObserver;
+import android.os.Binder;
+import android.os.Handler;
+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 int startVoiceActivity(Intent intent, String resolvedType,
+                IVoiceInteractionService service,
+                IVoiceInteractionSession session, IVoiceInteractor interactor) {
+            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 {
+                    return mImpl.startVoiceActivityLocked(callingPid, callingUid,
+                            intent, resolvedType, session, interactor);
+                } finally {
+                    Binder.restoreCallingIdentity(caller);
+                }
+            }
+        }
+
+        @Override
+        public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+            mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);
+        }
+
+        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..af8ae1e
--- /dev/null
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.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 com.android.server.voiceinteraction;
+
+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.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.VoiceInteractionService;
+import android.util.Slog;
+import com.android.internal.app.IVoiceInteractor;
+
+class VoiceInteractionManagerServiceImpl {
+    final static String TAG = "VoiceInteractionServiceManager";
+
+    final Context mContext;
+    final Handler mHandler;
+    final Object mLock;
+    final int mUser;
+    final ComponentName mComponent;
+    final IActivityManager mAm;
+    boolean mBound = false;
+    IVoiceInteractionService mService;
+    IVoiceInteractionSession mActiveSession;
+    IVoiceInteractor mActiveInteractor;
+
+    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;
+        }
+    };
+
+    VoiceInteractionManagerServiceImpl(Context context, Handler handler, Object lock,
+            int userHandle, ComponentName service) {
+        mContext = context;
+        mHandler = handler;
+        mLock = lock;
+        mUser = userHandle;
+        mComponent = service;
+        mAm = ActivityManagerNative.getDefault();
+    }
+
+    public int startVoiceActivityLocked(int callingPid, int callingUid, Intent intent,
+            String resolvedType, IVoiceInteractionSession session, IVoiceInteractor interactor) {
+        if (session == null) {
+            throw new NullPointerException("session is null");
+        }
+        if (interactor == null) {
+            throw new NullPointerException("interactor is null");
+        }
+        if (mActiveSession != null) {
+            // XXX cancel current session.
+        }
+        intent.addCategory(Intent.CATEGORY_VOICE);
+        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
+        mActiveSession = session;
+        mActiveInteractor = interactor;
+        try {
+            return mAm.startVoiceActivity(mComponent.getPackageName(), callingPid, callingUid,
+                    intent, resolvedType, mActiveSession, mActiveInteractor,
+                    0, null, null, null, mUser);
+        } catch (RemoteException e) {
+            throw new IllegalStateException("Unexpected remote error", e);
+        }
+    }
+
+    void startLocked() {
+        Intent intent = new Intent(VoiceInteractionService.SERVICE_INTERFACE);
+        intent.setComponent(mComponent);
+        try {
+            ServiceInfo si = mContext.getPackageManager().getServiceInfo(mComponent, 0);
+            if (!android.Manifest.permission.BIND_VOICE_INTERACTION.equals(si.permission)) {
+                Slog.w(TAG, "Not using voice interaction service " + mComponent
+                        + ": does not require permission "
+                        + android.Manifest.permission.BIND_VOICE_INTERACTION);
+                return;
+            }
+        } catch (PackageManager.NameNotFoundException e) {
+            Slog.w(TAG, "Unable to find voice interaction service: " + mComponent, e);
+            return;
+        }
+        mContext.bindServiceAsUser(intent, mConnection,
+                Context.BIND_AUTO_CREATE, new UserHandle(mUser));
+        mBound = true;
+    }
+
+    void shutdownLocked() {
+        if (mBound) {
+            mContext.unbindService(mConnection);
+            mBound = false;
+        }
+    }
+}
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..9c5acf9
--- /dev/null
+++ b/tests/VoiceInteraction/AndroidManifest.xml
@@ -0,0 +1,27 @@
+<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">
+            <intent-filter>
+                <action android:name="android.service.voice.VoiceInteractionService" />
+            </intent-filter>
+        </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/src/com/android/test/voiceinteraction/MainInteractionService.java b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionService.java
new file mode 100644
index 0000000..35702f1
--- /dev/null
+++ b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionService.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.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),
+                new MainInteractionSession(this));
+        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..adc0df4
--- /dev/null
+++ b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionSession.java
@@ -0,0 +1,53 @@
+/*
+ * 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";
+
+    MainInteractionSession(Context context) {
+        super(context);
+    }
+
+    @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(null);
+    }
+
+    @Override
+    public void onCancel(Request request) {
+        Log.i(TAG, "onCancel");
+        request.sendCancelResult();
+    }
+}
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..016a80e
--- /dev/null
+++ b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/TestInteractionActivity.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.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);
+
+        setContentView(R.layout.test_interaction);
+        if (!isVoiceInteraction()) {
+            Log.w(TAG, "Not running as a voice interaction!");
+            finish();
+            return;
+        }
+
+        mInteractor = getVoiceInteractor();
+        mInteractor.startConfirmation(new VoiceInteractor.Callback() {
+            @Override
+            public void onConfirmationResult(VoiceInteractor.Request request, boolean confirmed,
+                    Bundle result) {
+                Log.i(TAG, "Confirmation result: confirmed=" + confirmed + " result=" + result);
+                finish();
+            }
+
+            @Override
+            public void onCancel(VoiceInteractor.Request request) {
+                Log.i(TAG, "Canceled!");
+                finish();
+            }
+        }, "This is a confirmation", null);
+    }
+
+    @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));
+        }
+    };
+}