Merge "Derive library path for upgraded system apps."
diff --git a/Android.mk b/Android.mk
index 6859678..a7eb03d 100644
--- a/Android.mk
+++ b/Android.mk
@@ -406,6 +406,8 @@
 # List of packages to exclude along with their descendants.
 # Overrides inclusion.
 LOCAL_JAR_EXCLUDE_PACKAGES := \
+    android.filterfw \
+    android.filterpacks \
     android.hardware
 
 # List of classes and interfaces which should be loaded by the Zygote.
@@ -426,6 +428,8 @@
 
 # List of packages to include along with their descendants.
 LOCAL_JAR_PACKAGES := \
+    android.filterfw \
+    android.filterpacks \
     android.hardware \
     com \
     javax
@@ -614,7 +618,6 @@
 	ext \
 	framework \
 	framework2 \
-	mms-common \
 	telephony-common \
 	voip-common
 
diff --git a/api/current.txt b/api/current.txt
index 92dce76..319f4b6 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -134,7 +134,6 @@
     field public static final java.lang.String SET_WALLPAPER = "android.permission.SET_WALLPAPER";
     field public static final java.lang.String SET_WALLPAPER_HINTS = "android.permission.SET_WALLPAPER_HINTS";
     field public static final java.lang.String SIGNAL_PERSISTENT_PROCESSES = "android.permission.SIGNAL_PERSISTENT_PROCESSES";
-    field public static final java.lang.String SIM_COMMUNICATION = "android.permission.SIM_COMMUNICATION";
     field public static final java.lang.String STATUS_BAR = "android.permission.STATUS_BAR";
     field public static final java.lang.String SUBSCRIBED_FEEDS_READ = "android.permission.SUBSCRIBED_FEEDS_READ";
     field public static final java.lang.String SUBSCRIBED_FEEDS_WRITE = "android.permission.SUBSCRIBED_FEEDS_WRITE";
@@ -3334,6 +3333,7 @@
     method public void onAttachFragment(android.app.Fragment);
     method public void onAttachedToWindow();
     method public void onBackPressed();
+    method public void onBackgroundMediaPlayingChanged(boolean);
     method protected void onChildTitleChanged(android.app.Activity, java.lang.CharSequence);
     method public void onConfigurationChanged(android.content.res.Configuration);
     method public void onContentChanged();
@@ -3389,6 +3389,7 @@
     method public boolean onSearchRequested();
     method protected void onStart();
     method protected void onStop();
+    method public void onStopMediaPlaying();
     method protected void onTitleChanged(java.lang.CharSequence, int);
     method public boolean onTouchEvent(android.view.MotionEvent);
     method public boolean onTrackballEvent(android.view.MotionEvent);
@@ -3423,6 +3424,7 @@
     method public void setFinishOnTouchOutside(boolean);
     method public void setImmersive(boolean);
     method public void setIntent(android.content.Intent);
+    method public boolean setMediaPlaying(boolean);
     method public final void setProgress(int);
     method public final void setProgressBarIndeterminate(boolean);
     method public final void setProgressBarIndeterminateVisibility(boolean);
@@ -16098,6 +16100,7 @@
 
   public abstract class TvInputService.Session implements android.view.KeyEvent.Callback {
     ctor public TvInputService.Session();
+    method public void dispatchChannelRetuned(android.net.Uri);
     method public android.view.View onCreateOverlayView();
     method public boolean onGenericMotionEvent(android.view.MotionEvent);
     method public boolean onKeyDown(int, android.view.KeyEvent);
@@ -16135,6 +16138,7 @@
 
   public static abstract class TvView.TvInputListener {
     ctor public TvView.TvInputListener();
+    method public void onChannelRetuned(java.lang.String, android.net.Uri);
     method public void onError(java.lang.String, int);
   }
 
@@ -16315,6 +16319,7 @@
     field public static final int TYPE_MOBILE_HIPRI = 5; // 0x5
     field public static final int TYPE_MOBILE_MMS = 2; // 0x2
     field public static final int TYPE_MOBILE_SUPL = 3; // 0x3
+    field public static final int TYPE_VPN = 17; // 0x11
     field public static final int TYPE_WIFI = 1; // 0x1
     field public static final int TYPE_WIMAX = 6; // 0x6
   }
@@ -27671,14 +27676,14 @@
   }
 
   public final class ConnectionRequest implements android.os.Parcelable {
-    ctor public ConnectionRequest(android.net.Uri, android.os.Bundle);
-    ctor public ConnectionRequest(java.lang.String, android.net.Uri, android.os.Bundle);
-    ctor public ConnectionRequest(android.telecomm.PhoneAccount, java.lang.String, android.net.Uri, android.os.Bundle);
+    ctor public ConnectionRequest(java.lang.String, android.net.Uri, android.os.Bundle, int);
+    ctor public ConnectionRequest(android.telecomm.PhoneAccount, java.lang.String, android.net.Uri, android.os.Bundle, int);
     method public int describeContents();
     method public android.telecomm.PhoneAccount getAccount();
     method public java.lang.String getCallId();
     method public android.os.Bundle getExtras();
     method public android.net.Uri getHandle();
+    method public int getVideoState();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator CREATOR;
   }
@@ -27851,7 +27856,7 @@
     field public static final java.lang.String EXTRA_CONNECTION_SERVICE = "android.telecomm.extra.CONNECTION_SERVICE";
     field public static final java.lang.String EXTRA_INCOMING_CALL_EXTRAS = "android.intent.extra.INCOMING_CALL_EXTRAS";
     field public static final java.lang.String EXTRA_START_CALL_WITH_SPEAKERPHONE = "android.intent.extra.START_CALL_WITH_SPEAKERPHONE";
-    field public static final java.lang.String EXTRA_START_CALL_WITH_VIDEO = "android.intent.extra.START_CALL_WITH_VIDEO";
+    field public static final java.lang.String EXTRA_START_CALL_WITH_VIDEO_STATE = "android.intent.extra.START_CALL_WITH_VIDEO_STATE";
   }
 
   public class VideoCallProfile implements android.os.Parcelable {
@@ -30379,6 +30384,40 @@
     method public void writeToParcel(android.os.Parcel, int);
   }
 
+  public class TtsSpan implements android.text.ParcelableSpan {
+    ctor public TtsSpan(java.lang.String, android.os.PersistableBundle);
+    ctor public TtsSpan(android.os.Parcel);
+    method public int describeContents();
+    method public android.os.PersistableBundle getArgs();
+    method public int getSpanTypeId();
+    method public java.lang.String getType();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final java.lang.String ANIMACY_ANIMATE = "android.animate";
+    field public static final java.lang.String ANIMACY_INANIMATE = "android.inanimate";
+    field public static final java.lang.String ARG_ANIMACY = "android.arg.animacy";
+    field public static final java.lang.String ARG_CASE = "android.arg.case";
+    field public static final java.lang.String ARG_GENDER = "android.arg.gender";
+    field public static final java.lang.String ARG_MULTIPLICITY = "android.arg.multiplicity";
+    field public static final java.lang.String ARG_NUMBER = "android.arg.number";
+    field public static final java.lang.String ARG_TEXT = "android.arg.text";
+    field public static final java.lang.String CASE_ABLATIVE = "android.ablative";
+    field public static final java.lang.String CASE_ACCUSATIVE = "android.accusative";
+    field public static final java.lang.String CASE_DATIVE = "android.dative";
+    field public static final java.lang.String CASE_GENITIVE = "android.genitive";
+    field public static final java.lang.String CASE_INSTRUMENTAL = "android.instrumental";
+    field public static final java.lang.String CASE_LOCATIVE = "android.locative";
+    field public static final java.lang.String CASE_NOMINATIVE = "android.nomative";
+    field public static final java.lang.String CASE_VOCATIVE = "android.vocative";
+    field public static final java.lang.String GENDER_FEMALE = "android.female";
+    field public static final java.lang.String GENDER_MALE = "android.male";
+    field public static final java.lang.String GENDER_NEUTRAL = "android.neutral";
+    field public static final java.lang.String MULTIPLICITY_DUAL = "android.dual";
+    field public static final java.lang.String MULTIPLICITY_PLURAL = "android.plural";
+    field public static final java.lang.String MULTIPLICITY_SINGLE = "android.single";
+    field public static final java.lang.String TYPE_CARDINAL = "android.type.cardinal";
+    field public static final java.lang.String TYPE_TEXT = "android.type.text";
+  }
+
   public class TypefaceSpan extends android.text.style.MetricAffectingSpan implements android.text.ParcelableSpan {
     ctor public TypefaceSpan(java.lang.String);
     ctor public TypefaceSpan(android.os.Parcel);
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 2914a61..f1a2576 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -743,10 +743,13 @@
             return Activity.this.findViewById(id);
         }
     };
-    
+
+    // Most recent call to setMediaPlaying().
+    boolean mMediaPlaying;
+
     ArrayMap<String, LoaderManagerImpl> mAllLoaderManagers;
     LoaderManagerImpl mLoaderManager;
-    
+
     private static final class ManagedCursor {
         ManagedCursor(Cursor cursor) {
             mCursor = cursor;
@@ -764,6 +767,7 @@
     // protected by synchronized (this) 
     int mResultCode = RESULT_CANCELED;
     Intent mResultData = null;
+
     private TranslucentConversionListener mTranslucentCallback;
     private boolean mChangeCanvasToTranslucent;
 
@@ -3704,9 +3708,6 @@
      * @see #startActivity
      */
     public void startActivityForResult(Intent intent, int requestCode, @Nullable Bundle options) {
-        if (options != null) {
-            mActivityTransitionState.startExitOutTransition(this, options);
-        }
         if (mParent == null) {
             Instrumentation.ActivityResult ar =
                 mInstrumentation.execStartActivity(
@@ -3742,6 +3743,9 @@
                 mParent.startActivityFromChild(this, intent, requestCode);
             }
         }
+        if (options != null && !isTopOfTask()) {
+            mActivityTransitionState.startExitOutTransition(this, options);
+        }
     }
 
     /**
@@ -5207,9 +5211,8 @@
      * another task.
      *
      * @return true if this is the topmost, non-finishing activity in its task.
-     * @hide
      */
-    public boolean isTopOfTask() {
+    private boolean isTopOfTask() {
         try {
             return ActivityManagerNative.getDefault().isTopOfTask(mToken);
         } catch (RemoteException e) {
@@ -5325,6 +5328,101 @@
     }
 
     /**
+     * Activities that want to show media behind a translucent activity above them must call this
+     * method anytime before a return from {@link #onPause()}. If this call is successful
+     * then the activity should continue to play media when {@link #onPause()} is called, but must
+     * stop playing and release resources prior to or within the call to
+     * {@link #onStopMediaPlaying()}. If this call returns false the activity must stop
+     * playing and release resources immediately.
+     *
+     * <p>Only fullscreen opaque activities may make this call. I.e. this call is a nop
+     * for dialog and translucent activities.
+     *
+     * <p>False will be returned any time this method is call between the return of onPause and
+     *      the next call to onResume.
+     *
+     * @param playing true to notify the system that media is starting or continuing playing,
+     *                false to indicate that media has stopped or is stopping. Resources must
+     *                be released when passing false to this method.
+     * @return the resulting play state. If true the activity may continue playing media beyond
+     *      {@link #onPause()}, if false then the caller must stop playing and immediately
+     *      release all media resources. Returning false may occur in lieu of a call to
+     *      onReleaseMediaResources() so the return value must be checked.
+     *
+     * @see #isBackgroundMediaPlaying()
+     * @see #onStopMediaPlaying()
+     * @see #onBackgroundMediaPlayingChanged(boolean)
+     */
+    public boolean setMediaPlaying(boolean playing) {
+        if (!mResumed) {
+            // Do not permit paused or stopped activities to start playing.
+            playing = false;
+        }
+        try {
+            mMediaPlaying = ActivityManagerNative.getDefault().setMediaPlaying(mToken, playing) &&
+                    playing;
+        } catch (RemoteException e) {
+            mMediaPlaying = false;
+        }
+        return mMediaPlaying;
+    }
+
+    /**
+     * Called when a translucent activity over playing media is becoming opaque or another
+     * activity is being launched. Activities that call {@link #setMediaPlaying(boolean)}
+     * must implement this method to at the minimum call
+     * <code>super.onStopMediaPlayback()</code>.
+     *
+     * <p>When this method is called the activity has 500 msec to release the media resources.
+     * If the activity has not returned from this method in 500 msec the system will destroy
+     * the activity and kill the process in order to recover the media resources for another
+     * process. Otherwise {@link #onStop()} will be called following return.
+     *
+     * @see #setMediaPlaying(boolean)
+     * @see #isBackgroundMediaPlaying()
+     * @see #onBackgroundMediaPlayingChanged(boolean)
+     */
+    public void onStopMediaPlaying() {
+        mCalled = true;
+    }
+
+    /**
+     * Translucent activities may call this to determine if there is an activity below it that
+     * is playing media.
+     *
+     * @return true if media is playing according to the most recent call to
+     * {@link #setMediaPlaying(boolean)}, false otherwise.
+     *
+     * @see #setMediaPlaying(boolean)
+     * @see #onStopMediaPlaying()
+     * @see #onBackgroundMediaPlayingChanged(boolean)
+     * @hide
+     */
+    public boolean isBackgroundMediaPlaying() {
+        try {
+            return ActivityManagerNative.getDefault().isBackgroundMediaPlaying(mToken);
+        } catch (RemoteException e) {
+        }
+        return false;
+    }
+
+    /**
+     * The topmost foreground activity will receive this call when an activity below it either
+     * starts or stops playing media.
+     *
+     * This call may be a consequence of {@link #setMediaPlaying(boolean)} or might be
+     * due to a background activity finishing itself.
+     *
+     * @param playing true if media playback is starting, false if it is stopping.
+     *
+     * @see #setMediaPlaying(boolean)
+     * @see #isBackgroundMediaPlaying()
+     * @see #onStopMediaPlaying()
+     */
+    public void onBackgroundMediaPlayingChanged(boolean playing) {
+    }
+
+    /**
      * Adjust the current immersive mode setting.
      *
      * Note that changing this value will have no effect on the activity's
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index ce36fd8..4a70e15 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -2176,6 +2176,33 @@
             reply.writeNoException();
             return true;
         }
+
+        case SET_MEDIA_PLAYING_TRANSACTION: {
+            data.enforceInterface(IActivityManager.descriptor);
+            IBinder token = data.readStrongBinder();
+            boolean enable = data.readInt() > 0;
+            boolean success = setMediaPlaying(token, enable);
+            reply.writeNoException();
+            reply.writeInt(success ? 1 : 0);
+            return true;
+        }
+
+        case IS_BG_MEDIA_PLAYING_TRANSACTION: {
+            data.enforceInterface(IActivityManager.descriptor);
+            IBinder token = data.readStrongBinder();
+            final boolean enabled = isBackgroundMediaPlaying(token);
+            reply.writeNoException();
+            reply.writeInt(enabled ? 1 : 0);
+            return true;
+        }
+
+        case MEDIA_RESOURCES_RELEASED: {
+            data.enforceInterface(IActivityManager.descriptor);
+            IBinder token = data.readStrongBinder();
+            mediaResourcesReleased(token);
+            reply.writeNoException();
+            return true;
+        }
         }
 
         return super.onTransact(code, data, reply, flags);
@@ -5007,5 +5034,46 @@
         reply.recycle();
     }
 
+    @Override
+    public boolean setMediaPlaying(IBinder token, boolean playing) throws RemoteException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeInterfaceToken(IActivityManager.descriptor);
+        data.writeStrongBinder(token);
+        data.writeInt(playing ? 1 : 0);
+        mRemote.transact(SET_MEDIA_PLAYING_TRANSACTION, data, reply, 0);
+        reply.readException();
+        boolean success = reply.readInt() > 0;
+        data.recycle();
+        reply.recycle();
+        return success;
+    }
+
+    @Override
+    public boolean isBackgroundMediaPlaying(IBinder token) throws RemoteException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeInterfaceToken(IActivityManager.descriptor);
+        data.writeStrongBinder(token);
+        mRemote.transact(IS_BG_MEDIA_PLAYING_TRANSACTION, data, reply, 0);
+        reply.readException();
+        final boolean playing = reply.readInt() > 0;
+        data.recycle();
+        reply.recycle();
+        return playing;
+    }
+
+    @Override
+    public void mediaResourcesReleased(IBinder token) throws RemoteException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeInterfaceToken(IActivityManager.descriptor);
+        data.writeStrongBinder(token);
+        mRemote.transact(MEDIA_RESOURCES_RELEASED, data, reply, IBinder.FLAG_ONEWAY);
+        reply.readException();
+        data.recycle();
+        reply.recycle();
+    }
+
     private IBinder mRemote;
 }
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 7b48e1d..184e10c 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -1153,6 +1153,16 @@
         public final void updateTimePrefs(boolean is24Hour) {
             DateFormat.set24HourTimePref(is24Hour);
         }
+
+        @Override
+        public void scheduleStopMediaPlaying(IBinder token) {
+            sendMessage(H.STOP_MEDIA_PLAYING, token);
+        }
+
+        @Override
+        public void scheduleBackgroundMediaPlayingChanged(IBinder token, boolean playing) {
+            sendMessage(H.BACKGROUND_MEDIA_PLAYING_CHANGED, token, playing ? 1 : 0);
+        }
     }
 
     private class H extends Handler {
@@ -1203,6 +1213,8 @@
         public static final int TRANSLUCENT_CONVERSION_COMPLETE = 144;
         public static final int INSTALL_PROVIDER        = 145;
         public static final int ON_NEW_ACTIVITY_OPTIONS = 146;
+        public static final int STOP_MEDIA_PLAYING = 147;
+        public static final int BACKGROUND_MEDIA_PLAYING_CHANGED = 148;
 
         String codeToString(int code) {
             if (DEBUG_MESSAGES) {
@@ -1253,6 +1265,8 @@
                     case TRANSLUCENT_CONVERSION_COMPLETE: return "TRANSLUCENT_CONVERSION_COMPLETE";
                     case INSTALL_PROVIDER: return "INSTALL_PROVIDER";
                     case ON_NEW_ACTIVITY_OPTIONS: return "ON_NEW_ACTIVITY_OPTIONS";
+                    case STOP_MEDIA_PLAYING: return "STOP_MEDIA_PLAYING";
+                    case BACKGROUND_MEDIA_PLAYING_CHANGED: return "BACKGROUND_MEDIA_PLAYING_CHANGED";
                 }
             }
             return Integer.toString(code);
@@ -1470,6 +1484,11 @@
                 case ON_NEW_ACTIVITY_OPTIONS:
                     Pair<IBinder, ActivityOptions> pair = (Pair<IBinder, ActivityOptions>) msg.obj;
                     onNewActivityOptions(pair.first, pair.second);
+                case STOP_MEDIA_PLAYING:
+                    handleStopMediaPlaying((IBinder) msg.obj);
+                    break;
+                case BACKGROUND_MEDIA_PLAYING_CHANGED:
+                    handleOnBackgroundMediaPlayingChanged((IBinder) msg.obj, msg.arg1 > 0);
                     break;
             }
             if (DEBUG_MESSAGES) Slog.v(TAG, "<<< done: " + codeToString(msg.what));
@@ -2454,6 +2473,34 @@
         }
     }
 
+    public void handleStopMediaPlaying(IBinder token) {
+        ActivityClientRecord r = mActivities.get(token);
+        if (r != null) {
+            final Activity activity = r.activity;
+            if (activity.mMediaPlaying) {
+                activity.mCalled = false;
+                activity.onStopMediaPlaying();
+                // Tick, tick, tick. The activity has 500 msec to return or it will be destroyed.
+                if (!activity.mCalled) {
+                    throw new SuperNotCalledException("Activity " + activity.getLocalClassName() +
+                            " did not call through to super.onStopMediaPlayback()");
+                }
+                activity.mMediaPlaying = false;
+            }
+        }
+        try {
+            ActivityManagerNative.getDefault().mediaResourcesReleased(token);
+        } catch (RemoteException e) {
+        }
+    }
+
+    public void handleOnBackgroundMediaPlayingChanged(IBinder token, boolean playing) {
+        ActivityClientRecord r = mActivities.get(token);
+        if (r != null) {
+            r.activity.onBackgroundMediaPlayingChanged(playing);
+        }
+    }
+
     public void handleInstallProvider(ProviderInfo info) {
         installContentProviders(mInitialApplication, Lists.newArrayList(info));
     }
diff --git a/core/java/android/app/ActivityTransitionCoordinator.java b/core/java/android/app/ActivityTransitionCoordinator.java
index 6c5c98b..1a03fe9 100644
--- a/core/java/android/app/ActivityTransitionCoordinator.java
+++ b/core/java/android/app/ActivityTransitionCoordinator.java
@@ -274,8 +274,8 @@
         return names;
     }
 
-    public ArrayList<View> getMappedViews() {
-        return mSharedElements;
+    public ArrayList<View> copyMappedViews() {
+        return new ArrayList<View>(mSharedElements);
     }
 
     public ArrayList<String> getAllSharedElementNames() { return mAllSharedElementNames; }
@@ -521,9 +521,7 @@
     protected void clearState() {
         // Clear the state so that we can't hold any references accidentally and leak memory.
         mWindow = null;
-        mAllSharedElementNames.clear();
         mSharedElements.clear();
-        mSharedElementNames.clear();
         mTransitioningViews.clear();
         mResultReceiver = null;
         mPendingTransition = null;
diff --git a/core/java/android/app/ActivityTransitionState.java b/core/java/android/app/ActivityTransitionState.java
index 0304246..0d2af8c 100644
--- a/core/java/android/app/ActivityTransitionState.java
+++ b/core/java/android/app/ActivityTransitionState.java
@@ -240,7 +240,7 @@
             if (!mHasExited) {
                 mHasExited = true;
                 if (mEnterTransitionCoordinator != null) {
-                    mEnterTransitionCoordinator.stop();
+                    mEnterTransitionCoordinator.cancelEnter();
                     mEnterTransitionCoordinator = null;
                 }
                 ArrayMap<String, View> sharedElements = new ArrayMap<String, View>();
@@ -268,7 +268,7 @@
                 if (mCalledExitCoordinator != null) {
                     mExitingFrom = mCalledExitCoordinator.getAcceptedNames();
                     mExitingTo = mCalledExitCoordinator.getMappedNames();
-                    mExitingToView = mCalledExitCoordinator.getMappedViews();
+                    mExitingToView = mCalledExitCoordinator.copyMappedViews();
                     mCalledExitCoordinator.startExit();
                 }
             }
diff --git a/core/java/android/app/ApplicationThreadNative.java b/core/java/android/app/ApplicationThreadNative.java
index 6dead08..0b4510fe 100644
--- a/core/java/android/app/ApplicationThreadNative.java
+++ b/core/java/android/app/ApplicationThreadNative.java
@@ -647,6 +647,25 @@
             reply.writeNoException();
             return true;
         }
+
+        case STOP_MEDIA_PLAYING_TRANSACTION:
+        {
+            data.enforceInterface(IApplicationThread.descriptor);
+            IBinder token = data.readStrongBinder();
+            scheduleStopMediaPlaying(token);
+            reply.writeNoException();
+            return true;
+        }
+
+        case BACKGROUND_MEDIA_PLAYING_CHANGED_TRANSACTION:
+        {
+            data.enforceInterface(IApplicationThread.descriptor);
+            IBinder token = data.readStrongBinder();
+            boolean enabled = data.readInt() > 0;
+            scheduleBackgroundMediaPlayingChanged(token, enabled);
+            reply.writeNoException();
+            return true;
+        }
         }
 
         return super.onTransact(code, data, reply, flags);
@@ -1304,4 +1323,23 @@
         mRemote.transact(UPDATE_TIME_PREFS_TRANSACTION, data, null, IBinder.FLAG_ONEWAY);
         data.recycle();
     }
+
+    @Override
+    public void scheduleStopMediaPlaying(IBinder token) throws RemoteException {
+        Parcel data = Parcel.obtain();
+        data.writeInterfaceToken(IApplicationThread.descriptor);
+        data.writeStrongBinder(token);
+        mRemote.transact(STOP_MEDIA_PLAYING_TRANSACTION, data, null, IBinder.FLAG_ONEWAY);
+        data.recycle();
+    }
+
+    @Override
+    public void scheduleBackgroundMediaPlayingChanged(IBinder token, boolean enabled) throws RemoteException {
+        Parcel data = Parcel.obtain();
+        data.writeInterfaceToken(IApplicationThread.descriptor);
+        data.writeStrongBinder(token);
+        data.writeInt(enabled ? 1 : 0);
+        mRemote.transact(BACKGROUND_MEDIA_PLAYING_CHANGED_TRANSACTION, data, null, IBinder.FLAG_ONEWAY);
+        data.recycle();
+    }
 }
diff --git a/core/java/android/app/EnterTransitionCoordinator.java b/core/java/android/app/EnterTransitionCoordinator.java
index 5e18d0f..90f36b8 100644
--- a/core/java/android/app/EnterTransitionCoordinator.java
+++ b/core/java/android/app/EnterTransitionCoordinator.java
@@ -58,6 +58,7 @@
     private Bundle mSharedElementsBundle;
     private boolean mWasOpaque;
     private boolean mAreViewsReady;
+    private boolean mIsViewsTransitionStarted;
 
     public EnterTransitionCoordinator(Activity activity, ResultReceiver resultReceiver,
             ArrayList<String> sharedElementNames, boolean isReturning) {
@@ -219,7 +220,7 @@
     private void cancel() {
         if (!mIsCanceled) {
             mIsCanceled = true;
-            if (getViewsTransition() == null) {
+            if (getViewsTransition() == null || mIsViewsTransitionStarted) {
                 setViewVisibility(mSharedElements, View.VISIBLE);
             } else {
                 mTransitioningViews.addAll(mSharedElements);
@@ -363,6 +364,7 @@
                 stripOffscreenViews();
             }
         }
+        mIsViewsTransitionStarted = mIsViewsTransitionStarted || startEnterTransition;
 
         Transition transition = mergeTransitions(sharedElementTransition, viewsTransition);
         if (startSharedElementTransition) {
@@ -433,6 +435,17 @@
 
     public void stop() {
         makeOpaque();
+        mIsCanceled = true;
+        mResultReceiver = null;
+        if (mBackgroundAnimator != null) {
+            mBackgroundAnimator.end();
+            mBackgroundAnimator = null;
+        }
+        mActivity = null;
+        clearState();
+    }
+
+    public void cancelEnter() {
         mHasStopped = true;
         mIsCanceled = true;
         mResultReceiver = null;
@@ -440,6 +453,8 @@
             mBackgroundAnimator.cancel();
             mBackgroundAnimator = null;
         }
+        mActivity = null;
+        clearState();
     }
 
     private void makeOpaque() {
diff --git a/core/java/android/app/ExitTransitionCoordinator.java b/core/java/android/app/ExitTransitionCoordinator.java
index 93cba0d..0fbd685 100644
--- a/core/java/android/app/ExitTransitionCoordinator.java
+++ b/core/java/android/app/ExitTransitionCoordinator.java
@@ -66,6 +66,8 @@
 
     private Bundle mExitSharedElementBundle;
 
+    private boolean mIsExitStarted;
+
     public ExitTransitionCoordinator(Activity activity, ArrayList<String> names,
             ArrayList<String> accepted, ArrayList<View> mapped, boolean isReturning) {
         super(activity.getWindow(), names, getListener(activity, isReturning),
@@ -114,6 +116,7 @@
         setViewVisibility(mTransitioningViews, View.VISIBLE);
         setViewVisibility(mSharedElements, View.VISIBLE);
         mIsHidden = true;
+        clearState();
     }
 
     private void sharedElementExitBack() {
@@ -164,46 +167,52 @@
     }
 
     public void startExit() {
-        startTransition(new Runnable() {
-            @Override
-            public void run() {
-                beginTransitions();
-            }
-        });
-        setViewVisibility(mTransitioningViews, View.INVISIBLE);
+        if (!mIsExitStarted) {
+            mIsExitStarted = true;
+            startTransition(new Runnable() {
+                @Override
+                public void run() {
+                    beginTransitions();
+                }
+            });
+            setViewVisibility(mTransitioningViews, View.INVISIBLE);
+        }
     }
 
     public void startExit(int resultCode, Intent data) {
-        mHandler = new Handler() {
-            @Override
-            public void handleMessage(Message msg) {
-                mIsCanceled = true;
-                finish();
-            }
-        };
-        mHandler.sendEmptyMessageDelayed(MSG_CANCEL, MAX_WAIT_MS);
-        if (getDecor().getBackground() == null) {
-            ColorDrawable black = new ColorDrawable(0xFF000000);
-            black.setAlpha(0);
-            getWindow().setBackgroundDrawable(black);
-            black.setAlpha(255);
-        }
-        ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation(mActivity, this,
-                mAllSharedElementNames, resultCode, data);
-        mActivity.convertToTranslucent(new Activity.TranslucentConversionListener() {
-            @Override
-            public void onTranslucentConversionComplete(boolean drawComplete) {
-                if (!mIsCanceled) {
-                    fadeOutBackground();
+        if (!mIsExitStarted) {
+            mIsExitStarted = true;
+            mHandler = new Handler() {
+                @Override
+                public void handleMessage(Message msg) {
+                    mIsCanceled = true;
+                    finish();
                 }
+            };
+            mHandler.sendEmptyMessageDelayed(MSG_CANCEL, MAX_WAIT_MS);
+            if (getDecor().getBackground() == null) {
+                ColorDrawable black = new ColorDrawable(0xFF000000);
+                black.setAlpha(0);
+                getWindow().setBackgroundDrawable(black);
+                black.setAlpha(255);
             }
-        }, options);
-        startTransition(new Runnable() {
-            @Override
-            public void run() {
-                startExitTransition();
-            }
-        });
+            ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation(mActivity, this,
+                    mAllSharedElementNames, resultCode, data);
+            mActivity.convertToTranslucent(new Activity.TranslucentConversionListener() {
+                @Override
+                public void onTranslucentConversionComplete(boolean drawComplete) {
+                    if (!mIsCanceled) {
+                        fadeOutBackground();
+                    }
+                }
+            }, options);
+            startTransition(new Runnable() {
+                @Override
+                public void run() {
+                    startExitTransition();
+                }
+            });
+        }
     }
 
     private void startExitTransition() {
@@ -216,20 +225,23 @@
 
     private void fadeOutBackground() {
         if (mBackgroundAnimator == null) {
-            Drawable background = getDecor().getBackground();
-            mBackgroundAnimator = ObjectAnimator.ofInt(background, "alpha", 0);
-            mBackgroundAnimator.addListener(new AnimatorListenerAdapter() {
-                @Override
-                public void onAnimationEnd(Animator animation) {
-                    mBackgroundAnimator = null;
-                    if (!mIsCanceled) {
-                        mIsBackgroundReady = true;
-                        notifyComplete();
+            ViewGroup decor = getDecor();
+            Drawable background;
+            if (decor != null && (background = decor.getBackground()) != null) {
+                mBackgroundAnimator = ObjectAnimator.ofInt(background, "alpha", 0);
+                mBackgroundAnimator.addListener(new AnimatorListenerAdapter() {
+                    @Override
+                    public void onAnimationEnd(Animator animation) {
+                        mBackgroundAnimator = null;
+                        if (!mIsCanceled) {
+                            mIsBackgroundReady = true;
+                            notifyComplete();
+                        }
                     }
-                }
-            });
-            mBackgroundAnimator.setDuration(getFadeDuration());
-            mBackgroundAnimator.start();
+                });
+                mBackgroundAnimator.setDuration(getFadeDuration());
+                mBackgroundAnimator.start();
+            }
         }
     }
 
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 6e4192b..b3a8a8a 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -415,11 +415,9 @@
 
     public void performIdleMaintenance() throws RemoteException;
 
-    /** @hide */
     public IActivityContainer createActivityContainer(IBinder parentActivityToken,
             IActivityContainerCallback callback) throws RemoteException;
 
-    /** @hide */
     public void deleteActivityContainer(IActivityContainer container) throws RemoteException;
 
     public IActivityContainer getEnclosingActivityContainer(IBinder activityToken)
@@ -427,28 +425,25 @@
 
     public IBinder getHomeActivityToken() throws RemoteException;
 
-    /** @hide */
     public void startLockTaskModeOnCurrent() throws RemoteException;
 
-    /** @hide */
     public void startLockTaskMode(int taskId) throws RemoteException;
 
-    /** @hide */
     public void startLockTaskMode(IBinder token) throws RemoteException;
 
-    /** @hide */
     public void stopLockTaskMode() throws RemoteException;
 
-    /** @hide */
     public void stopLockTaskModeOnCurrent() throws RemoteException;
 
-    /** @hide */
     public boolean isInLockTaskMode() throws RemoteException;
 
-    /** @hide */
     public void setTaskDescription(IBinder token, ActivityManager.TaskDescription values)
             throws RemoteException;
 
+    public boolean setMediaPlaying(IBinder token, boolean playing) throws RemoteException;
+    public boolean isBackgroundMediaPlaying(IBinder token) throws RemoteException;
+    public void mediaResourcesReleased(IBinder token) throws RemoteException;
+
     /*
      * Private non-Binder interfaces
      */
@@ -753,4 +748,7 @@
     int STOP_LOCK_TASK_BY_CURRENT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+222;
     int FINISH_VOICE_TASK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+223;
     int IS_TOP_OF_TASK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+224;
+    int SET_MEDIA_PLAYING_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+225;
+    int IS_BG_MEDIA_PLAYING_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+226;
+    int MEDIA_RESOURCES_RELEASED = IBinder.FIRST_CALL_TRANSACTION+227;
 }
diff --git a/core/java/android/app/IApplicationThread.java b/core/java/android/app/IApplicationThread.java
index d3c4854..18faf0e 100644
--- a/core/java/android/app/IApplicationThread.java
+++ b/core/java/android/app/IApplicationThread.java
@@ -145,6 +145,8 @@
     void setProcessState(int state) throws RemoteException;
     void scheduleInstallProvider(ProviderInfo provider) throws RemoteException;
     void updateTimePrefs(boolean is24Hour) throws RemoteException;
+    void scheduleStopMediaPlaying(IBinder token) throws RemoteException;
+    void scheduleBackgroundMediaPlayingChanged(IBinder token, boolean enabled) throws RemoteException;
 
     String descriptor = "android.app.IApplicationThread";
 
@@ -199,4 +201,6 @@
     int SET_PROCESS_STATE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+49;
     int SCHEDULE_INSTALL_PROVIDER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+50;
     int UPDATE_TIME_PREFS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+51;
+    int STOP_MEDIA_PLAYING_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+52;
+    int BACKGROUND_MEDIA_PLAYING_CHANGED_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+53;
 }
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index c5eb356..61ed631 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -744,13 +744,6 @@
     public static final String EXTRA_PEOPLE = "android.people";
 
     /**
-     * @hide
-     * Extra added by NotificationManagerService to indicate whether
-     * the Notifications's score has been modified.
-     */
-    public static final String EXTRA_SCORE_MODIFIED = "android.scoreModified";
-
-    /**
      * {@link #extras} key: used to provide hints about the appropriateness of
      * displaying this notification as a heads-up notification.
      * @hide
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index d75304f..35e5050 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -20,6 +20,7 @@
 import android.annotation.SdkConstant.SdkConstantType;
 import android.bluetooth.le.BluetoothLeAdvertiser;
 import android.bluetooth.le.BluetoothLeScanner;
+import android.bluetooth.le.ScanResult;
 import android.content.Context;
 import android.os.Handler;
 import android.os.IBinder;
@@ -37,6 +38,7 @@
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Locale;
 import java.util.Map;
 import java.util.Set;
@@ -504,13 +506,7 @@
      */
     public BluetoothLeAdvertiser getBluetoothLeAdvertiser() {
         // TODO: Return null if this feature is not supported by hardware.
-        try {
-            IBluetoothGatt iGatt = mManagerService.getBluetoothGatt();
-            return new BluetoothLeAdvertiser(iGatt);
-        } catch (RemoteException e) {
-            Log.e(TAG, "failed to get BluetoothLeAdvertiser, error: " + e);
-            return null;
-        }
+        return new BluetoothLeAdvertiser(mManagerService);
     }
 
     /**
@@ -518,13 +514,7 @@
      */
     public BluetoothLeScanner getBluetoothLeScanner() {
         // TODO: Return null if BLE scan is not supported by hardware.
-        try {
-            IBluetoothGatt iGatt = mManagerService.getBluetoothGatt();
-            return new BluetoothLeScanner(iGatt);
-        } catch (RemoteException e) {
-            Log.e(TAG, "failed to get BluetoothLeScanner, error: " + e);
-            return null;
-        }
+        return new BluetoothLeScanner(mManagerService);
     }
 
     /**
@@ -2137,5 +2127,10 @@
         public void onConnectionCongested(String address, boolean congested) {
             // no op
         }
+
+        @Override
+        public void onBatchScanResults(List<ScanResult> results) {
+            // no op
+        }
     }
 }
diff --git a/core/java/android/bluetooth/BluetoothGatt.java b/core/java/android/bluetooth/BluetoothGatt.java
index 6c1a45e..4255cd4 100644
--- a/core/java/android/bluetooth/BluetoothGatt.java
+++ b/core/java/android/bluetooth/BluetoothGatt.java
@@ -16,6 +16,7 @@
 
 package android.bluetooth;
 
+import android.bluetooth.le.ScanResult;
 import android.content.Context;
 import android.os.ParcelUuid;
 import android.os.RemoteException;
@@ -627,6 +628,11 @@
                     Log.w(TAG, "Unhandled exception in callback", ex);
                 }
             }
+
+            @Override
+            public void onBatchScanResults(List<ScanResult> results) {
+                // no op
+            }
         };
 
     /*package*/ BluetoothGatt(Context context, IBluetoothGatt iGatt, BluetoothDevice device,
diff --git a/core/java/android/bluetooth/IBluetoothGatt.aidl b/core/java/android/bluetooth/IBluetoothGatt.aidl
index 273d76d..5ed10a6 100644
--- a/core/java/android/bluetooth/IBluetoothGatt.aidl
+++ b/core/java/android/bluetooth/IBluetoothGatt.aidl
@@ -38,6 +38,7 @@
     void startScanWithFilters(in int appIf, in boolean isServer,
                               in ScanSettings settings, in List<ScanFilter> filters);
     void stopScan(in int appIf, in boolean isServer);
+    void flushPendingBatchResults(in int appIf, in boolean isServer);
     void startMultiAdvertising(in int appIf,
                                in AdvertisementData advertiseData,
                                in AdvertisementData scanResponse,
diff --git a/core/java/android/bluetooth/IBluetoothGattCallback.aidl b/core/java/android/bluetooth/IBluetoothGattCallback.aidl
index 946a6f6..c18d357 100644
--- a/core/java/android/bluetooth/IBluetoothGattCallback.aidl
+++ b/core/java/android/bluetooth/IBluetoothGattCallback.aidl
@@ -16,7 +16,7 @@
 package android.bluetooth;
 
 import android.os.ParcelUuid;
-
+import android.bluetooth.le.ScanResult;
 
 /**
  * Callback definitions for interacting with BLE / GATT
@@ -27,6 +27,7 @@
     void onClientConnectionState(in int status, in int clientIf,
                                  in boolean connected, in String address);
     void onScanResult(in String address, in int rssi, in byte[] advData);
+    void onBatchScanResults(in List<ScanResult> batchResults);
     void onGetService(in String address, in int srvcType, in int srvcInstId,
                       in ParcelUuid srvcUuid);
     void onGetIncludedService(in String address, in int srvcType, in int srvcInstId,
diff --git a/core/java/android/bluetooth/le/AdvertiseCallback.java b/core/java/android/bluetooth/le/AdvertiseCallback.java
index f1334c2..59f8cdc 100644
--- a/core/java/android/bluetooth/le/AdvertiseCallback.java
+++ b/core/java/android/bluetooth/le/AdvertiseCallback.java
@@ -52,6 +52,12 @@
     public static final int ADVERTISE_FAILED_CONTROLLER_FAILURE = 5;
 
     /**
+     * Operation fails due to GATT service failure.
+     * @hide
+     */
+    public static final int ADVERTISE_FAILED_GATT_SERVICE_FAILURE = 6;
+
+    /**
      * Callback when advertising operation succeeds.
      *
      * @param settingsInEffect The actual settings used for advertising, which may be different from
diff --git a/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java b/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java
index c20b81b..a83d875 100644
--- a/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java
+++ b/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java
@@ -20,6 +20,7 @@
 import android.bluetooth.BluetoothGatt;
 import android.bluetooth.IBluetoothGatt;
 import android.bluetooth.IBluetoothGattCallback;
+import android.bluetooth.IBluetoothManager;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.ParcelUuid;
@@ -27,6 +28,7 @@
 import android.util.Log;
 
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.UUID;
 
@@ -47,7 +49,7 @@
 
     private static final String TAG = "BluetoothLeAdvertiser";
 
-    private final IBluetoothGatt mBluetoothGatt;
+    private final IBluetoothManager mBluetoothManager;
     private final Handler mHandler;
     private final Map<AdvertiseCallback, AdvertiseCallbackWrapper>
             mLeAdvertisers = new HashMap<AdvertiseCallback, AdvertiseCallbackWrapper>();
@@ -55,11 +57,11 @@
     /**
      * Use BluetoothAdapter.getLeAdvertiser() instead.
      *
-     * @param bluetoothGatt
+     * @param bluetoothManager
      * @hide
      */
-    public BluetoothLeAdvertiser(IBluetoothGatt bluetoothGatt) {
-        mBluetoothGatt = bluetoothGatt;
+    public BluetoothLeAdvertiser(IBluetoothManager bluetoothManager) {
+        mBluetoothManager = bluetoothManager;
         mHandler = new Handler(Looper.getMainLooper());
     }
 
@@ -102,11 +104,19 @@
             postCallbackFailure(callback, AdvertiseCallback.ADVERTISE_FAILED_ALREADY_STARTED);
             return;
         }
+        IBluetoothGatt gatt;
+        try {
+            gatt = mBluetoothManager.getBluetoothGatt();
+        } catch (RemoteException e) {
+            Log.e(TAG, "failed to get bluetooth gatt - ", e);
+            postCallbackFailure(callback, AdvertiseCallback.ADVERTISE_FAILED_CONTROLLER_FAILURE);
+            return;
+        }
         AdvertiseCallbackWrapper wrapper = new AdvertiseCallbackWrapper(callback, advertiseData,
-                scanResponse, settings, mBluetoothGatt);
+                scanResponse, settings, gatt);
         UUID uuid = UUID.randomUUID();
         try {
-            mBluetoothGatt.registerClient(new ParcelUuid(uuid), wrapper);
+            gatt.registerClient(new ParcelUuid(uuid), wrapper);
             if (wrapper.advertiseStarted()) {
                 mLeAdvertisers.put(callback, wrapper);
             }
@@ -133,12 +143,19 @@
             return;
         }
         try {
-            mBluetoothGatt.stopMultiAdvertising(wrapper.mLeHandle);
+            IBluetoothGatt gatt = mBluetoothManager.getBluetoothGatt();
+            if (gatt == null) {
+                postCallbackFailure(callback,
+                        AdvertiseCallback.ADVERTISE_FAILED_GATT_SERVICE_FAILURE);
+            }
+            gatt.stopMultiAdvertising(wrapper.mLeHandle);
             if (wrapper.advertiseStopped()) {
                 mLeAdvertisers.remove(callback);
             }
         } catch (RemoteException e) {
             Log.e(TAG, "failed to stop advertising", e);
+            postCallbackFailure(callback,
+                    AdvertiseCallback.ADVERTISE_FAILED_GATT_SERVICE_FAILURE);
         }
     }
 
@@ -214,8 +231,6 @@
                         Log.e(TAG, "fail to start le advertise: " + e);
                         mLeHandle = -1;
                         notifyAll();
-                    } catch (Exception e) {
-                        Log.e(TAG, "fail to start advertise: " + e.getStackTrace());
                     }
                 } else {
                     // registration failed
@@ -360,6 +375,11 @@
         public void onConnectionCongested(String address, boolean congested) {
             // no op
         }
+
+        @Override
+        public void onBatchScanResults(List<ScanResult> results) {
+            // no op
+        }
     }
 
     private void postCallbackFailure(final AdvertiseCallback callback, final int error) {
diff --git a/core/java/android/bluetooth/le/BluetoothLeScanner.java b/core/java/android/bluetooth/le/BluetoothLeScanner.java
index fbaf5d2..44aa117 100644
--- a/core/java/android/bluetooth/le/BluetoothLeScanner.java
+++ b/core/java/android/bluetooth/le/BluetoothLeScanner.java
@@ -21,6 +21,7 @@
 import android.bluetooth.BluetoothGatt;
 import android.bluetooth.IBluetoothGatt;
 import android.bluetooth.IBluetoothGattCallback;
+import android.bluetooth.IBluetoothManager;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.ParcelUuid;
@@ -51,15 +52,15 @@
     private static final String TAG = "BluetoothLeScanner";
     private static final boolean DBG = true;
 
-    private final IBluetoothGatt mBluetoothGatt;
+    private final IBluetoothManager mBluetoothManager;
     private final Handler mHandler;
     private final Map<ScanCallback, BleScanCallbackWrapper> mLeScanClients;
 
     /**
      * @hide
      */
-    public BluetoothLeScanner(IBluetoothGatt bluetoothGatt) {
-        mBluetoothGatt = bluetoothGatt;
+    public BluetoothLeScanner(IBluetoothManager bluetoothManager) {
+        mBluetoothManager = bluetoothManager;
         mHandler = new Handler(Looper.getMainLooper());
         mLeScanClients = new HashMap<ScanCallback, BleScanCallbackWrapper>();
     }
@@ -84,11 +85,21 @@
                 postCallbackError(callback, ScanCallback.SCAN_FAILED_ALREADY_STARTED);
                 return;
             }
-            BleScanCallbackWrapper wrapper = new BleScanCallbackWrapper(mBluetoothGatt, filters,
+            IBluetoothGatt gatt;
+            try {
+                gatt = mBluetoothManager.getBluetoothGatt();
+            } catch (RemoteException e) {
+                gatt = null;
+            }
+            if (gatt == null) {
+                postCallbackError(callback, ScanCallback.SCAN_FAILED_GATT_SERVICE_FAILURE);
+                return;
+            }
+            BleScanCallbackWrapper wrapper = new BleScanCallbackWrapper(gatt, filters,
                     settings, callback);
             try {
                 UUID uuid = UUID.randomUUID();
-                mBluetoothGatt.registerClient(new ParcelUuid(uuid), wrapper);
+                gatt.registerClient(new ParcelUuid(uuid), wrapper);
                 if (wrapper.scanStarted()) {
                     mLeScanClients.put(callback, wrapper);
                 } else {
@@ -131,18 +142,26 @@
     }
 
     /**
-     * Poll scan results from bluetooth controller. This will return Bluetooth LE scan results
-     * batched on bluetooth controller.
+     * Flush pending batch scan results stored in Bluetooth controller. This will return Bluetooth
+     * LE scan results batched on bluetooth controller. Returns immediately, batch scan results data
+     * will be delivered through the {@code callback}.
      *
      * @param callback Callback of the Bluetooth LE Scan, it has to be the same instance as the one
      *            used to start scan.
-     * @param flush Whether to flush the batch scan buffer. Note the other batch scan clients will
-     *            get batch scan callback if the batch scan buffer is flushed.
-     * @return Batch Scan results.
-     * @hide TODO: unhide when batching is supported in stack.
+     *
+     * @hide
      */
-    public List<ScanResult> getBatchScanResults(ScanCallback callback, boolean flush) {
-        throw new UnsupportedOperationException("not impelemented");
+    public void flushPendingScanResults(ScanCallback callback) {
+        if (callback == null) {
+            throw new IllegalArgumentException("callback cannot be null!");
+        }
+        synchronized (mLeScanClients) {
+            BleScanCallbackWrapper wrapper = mLeScanClients.get(callback);
+            if (wrapper == null) {
+                return;
+            }
+            wrapper.flushPendingBatchResults();
+        }
     }
 
     /**
@@ -195,13 +214,27 @@
                     mBluetoothGatt.stopScan(mLeHandle, false);
                     mBluetoothGatt.unregisterClient(mLeHandle);
                 } catch (RemoteException e) {
-                    Log.e(TAG, "Failed to stop scan and unregister" + e);
+                    Log.e(TAG, "Failed to stop scan and unregister", e);
                 }
                 mLeHandle = -1;
                 notifyAll();
             }
         }
 
+        void flushPendingBatchResults() {
+            synchronized (this) {
+                if (mLeHandle <= 0) {
+                    Log.e(TAG, "Error state, mLeHandle: " + mLeHandle);
+                    return;
+                }
+                try {
+                    mBluetoothGatt.flushPendingBatchResults(mLeHandle, false);
+                } catch (RemoteException e) {
+                    Log.e(TAG, "Failed to get pending scan results", e);
+                }
+            }
+        }
+
         /**
          * Application interface registered - app is ready to go
          */
@@ -256,9 +289,27 @@
             BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(
                     address);
             long scanNanos = SystemClock.elapsedRealtimeNanos();
-            ScanResult result = new ScanResult(device, advData, rssi,
+            final ScanResult result = new ScanResult(device, advData, rssi,
                     scanNanos);
-            mScanCallback.onAdvertisementUpdate(result);
+            Handler handler = new Handler(Looper.getMainLooper());
+            handler.post(new Runnable() {
+                    @Override
+                public void run() {
+                    mScanCallback.onAdvertisementUpdate(result);
+                }
+            });
+
+        }
+
+        @Override
+        public void onBatchScanResults(final List<ScanResult> results) {
+            Handler handler = new Handler(Looper.getMainLooper());
+            handler.post(new Runnable() {
+                    @Override
+                public void run() {
+                    mScanCallback.onBatchScanResults(results);
+                }
+            });
         }
 
         @Override
diff --git a/core/java/android/bluetooth/le/ScanSettings.java b/core/java/android/bluetooth/le/ScanSettings.java
index 0a85675..3bb39b3 100644
--- a/core/java/android/bluetooth/le/ScanSettings.java
+++ b/core/java/android/bluetooth/le/ScanSettings.java
@@ -44,7 +44,6 @@
     public static final int CALLBACK_TYPE_ON_UPDATE = 0;
     /**
      * Callback when a bluetooth advertisement is found for the first time.
-     *
      * @hide
      */
     public static final int CALLBACK_TYPE_ON_FOUND = 1;
@@ -190,6 +189,7 @@
          *            {@link ScanSettings#SCAN_RESULT_TYPE_FULL} or
          *            {@link ScanSettings#SCAN_RESULT_TYPE_TRUNCATED}.
          * @throws IllegalArgumentException If the {@code scanResultType} is invalid.
+         *
          * @hide
          */
         public Builder setScanResultType(int scanResultType) {
diff --git a/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java b/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java
index 3ef538c..d505365 100644
--- a/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java
+++ b/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java
@@ -85,7 +85,9 @@
      * being set to true.
      */
     static final boolean LIE_ABOUT_AE_STATE = true;
+    static final boolean LIE_ABOUT_AE_MAX_REGIONS = true;
     static final boolean LIE_ABOUT_AF = true;
+    static final boolean LIE_ABOUT_AF_MAX_REGIONS = true;
     static final boolean LIE_ABOUT_AWB = true;
 
     /**
@@ -365,12 +367,23 @@
         /*
          * android.control.maxRegions
          */
+        final int AE = 0, AWB = 1, AF = 2;
+
         int[] maxRegions = new int[3];
-        maxRegions[0] = p.getMaxNumMeteringAreas();
-        maxRegions[1] = 0; // AWB regions not supported in API1
-        maxRegions[2] = p.getMaxNumFocusAreas();
+        maxRegions[AE] = p.getMaxNumMeteringAreas();
+        maxRegions[AWB] = 0; // AWB regions not supported in API1
+        maxRegions[AF] = p.getMaxNumFocusAreas();
+
+        if (LIE_ABOUT_AE_MAX_REGIONS) {
+            maxRegions[AE] = 0;
+        }
+        if (LIE_ABOUT_AF_MAX_REGIONS) {
+            maxRegions[AF] = 0;
+        }
+
         m.set(CONTROL_MAX_REGIONS, maxRegions);
-        // TODO
+
+        // TODO rest of control fields
     }
 
     private static void mapLens(CameraMetadataNative m, Camera.Parameters p) {
diff --git a/core/java/android/hardware/camera2/legacy/LegacyResultMapper.java b/core/java/android/hardware/camera2/legacy/LegacyResultMapper.java
index 8fbd41c..79287c1 100644
--- a/core/java/android/hardware/camera2/legacy/LegacyResultMapper.java
+++ b/core/java/android/hardware/camera2/legacy/LegacyResultMapper.java
@@ -121,6 +121,17 @@
             // TODO: Implement precapture trigger, after which we can report CONVERGED ourselves
             m.set(CONTROL_AE_STATE, CONTROL_AE_STATE_CONVERGED);
         }
+
+        // control.aeRegions
+
+        /*
+         * TODO: Use the *resulting* crop region to calculate intersection with
+         * metering region
+         *
+         * Report the sensor-relative metering region in the result even
+         * if that's not actually the real thing (similar to how we do it
+         * for zooming)
+         */
     }
 
 
diff --git a/core/java/android/hardware/soundtrigger/SoundTrigger.java b/core/java/android/hardware/soundtrigger/SoundTrigger.java
index 2d7af85..7a4e5a5 100644
--- a/core/java/android/hardware/soundtrigger/SoundTrigger.java
+++ b/core/java/android/hardware/soundtrigger/SoundTrigger.java
@@ -16,8 +16,6 @@
 
 package android.hardware.soundtrigger;
 
-import android.content.Context;
-import android.content.Intent;
 import android.os.Handler;
 
 import java.util.ArrayList;
@@ -65,7 +63,7 @@
         public final int maxSoundModels;
 
         /** Maximum number of key phrases */
-        public final int maxKeyPhrases;
+        public final int maxKeyphrases;
 
         /** Maximum number of users per key phrase */
         public final int maxUsers;
@@ -86,7 +84,7 @@
         public final int powerConsumptionMw;
 
         ModuleProperties(int id, String implementor, String description,
-                String uuid, int version, int maxSoundModels, int maxKeyPhrases,
+                String uuid, int version, int maxSoundModels, int maxKeyphrases,
                 int maxUsers, int recognitionModes, boolean supportsCaptureTransition,
                 int maxBufferMs, boolean supportsConcurrentCapture,
                 int powerConsumptionMw) {
@@ -96,7 +94,7 @@
             this.uuid = UUID.fromString(uuid);
             this.version = version;
             this.maxSoundModels = maxSoundModels;
-            this.maxKeyPhrases = maxKeyPhrases;
+            this.maxKeyphrases = maxKeyphrases;
             this.maxUsers = maxUsers;
             this.recognitionModes = recognitionModes;
             this.supportsCaptureTransition = supportsCaptureTransition;
@@ -109,7 +107,7 @@
     /*****************************************************************************
      * A SoundModel describes the attributes and contains the binary data used by the hardware
      * implementation to detect a particular sound pattern.
-     * A specialized version {@link KeyPhraseSoundModel} is defined for key phrase
+     * A specialized version {@link KeyphraseSoundModel} is defined for key phrase
      * sound models.
      ****************************************************************************/
     public static class SoundModel {
@@ -119,23 +117,30 @@
         /** Keyphrase sound model */
         public static final int TYPE_KEYPHRASE = 0;
 
+        /** Unique sound model identifier */
+        public final UUID uuid;
+
         /** Sound model type (e.g. TYPE_KEYPHRASE); */
         public final int type;
 
         /** Opaque data. For use by vendor implementation and enrollment application */
         public final byte[] data;
 
-        public SoundModel(int type, byte[] data) {
+        public SoundModel(UUID uuid, int type, byte[] data) {
+            this.uuid = uuid;
             this.type = type;
             this.data = data;
         }
     }
 
     /*****************************************************************************
-     * A KeyPhrase describes a key phrase that can be detected by a
-     * {@link KeyPhraseSoundModel}
+     * A Keyphrase describes a key phrase that can be detected by a
+     * {@link KeyphraseSoundModel}
      ****************************************************************************/
-    public static class KeyPhrase {
+    public static class Keyphrase {
+        /** Unique identifier for this keyphrase */
+        public final int id;
+
         /** Recognition modes supported for this key phrase in the model */
         public final int recognitionModes;
 
@@ -145,29 +150,31 @@
         /** Key phrase text */
         public final String text;
 
-        /** Number of users this key phrase has been trained for */
-        public final int numUsers;
+        /** Users this key phrase has been trained for. countains sound trigger specific user IDs
+         * derived from system user IDs {@link android.os.UserHandle#getIdentifier()}. */
+        public final int[] users;
 
-        public KeyPhrase(int recognitionModes, String locale, String text, int numUsers) {
+        public Keyphrase(int id, int recognitionModes, String locale, String text, int[] users) {
+            this.id = id;
             this.recognitionModes = recognitionModes;
             this.locale = locale;
             this.text = text;
-            this.numUsers = numUsers;
+            this.users = users;
         }
     }
 
     /*****************************************************************************
-     * A KeyPhraseSoundModel is a specialized {@link SoundModel} for key phrases.
+     * A KeyphraseSoundModel is a specialized {@link SoundModel} for key phrases.
      * It contains data needed by the hardware to detect a certain number of key phrases
-     * and the list of corresponding {@link KeyPhrase} descriptors.
+     * and the list of corresponding {@link Keyphrase} descriptors.
      ****************************************************************************/
-    public static class KeyPhraseSoundModel extends SoundModel {
+    public static class KeyphraseSoundModel extends SoundModel {
         /** Key phrases in this sound model */
-        public final KeyPhrase[] keyPhrases; // keyword phrases in model
+        public final Keyphrase[] keyphrases; // keyword phrases in model
 
-        public KeyPhraseSoundModel(byte[] data, KeyPhrase[] keyPhrases) {
-            super(TYPE_KEYPHRASE, data);
-            this.keyPhrases = keyPhrases;
+        public KeyphraseSoundModel(UUID id, byte[] data, Keyphrase[] keyphrases) {
+            super(id, TYPE_KEYPHRASE, data);
+            this.keyphrases = keyphrases;
         }
     }
 
@@ -209,55 +216,106 @@
         /** Delay in ms between end of model detection and start of audio available for capture.
          * A negative value is possible (e.g. if keyphrase is also available for capture) */
         public final int captureDelayMs;
+        /** Duration in ms of audio captured before the start of the trigger. 0 if none. */
+        public final int capturePreambleMs;
         /** Opaque data for use by system applications who know about voice engine internals,
          * typically during enrollment. */
         public final byte[] data;
 
         RecognitionEvent(int status, int soundModelHandle, boolean captureAvailable,
-                int captureSession, int captureDelayMs, byte[] data) {
+                int captureSession, int captureDelayMs, int capturePreambleMs, byte[] data) {
             this.status = status;
             this.soundModelHandle = soundModelHandle;
             this.captureAvailable = captureAvailable;
             this.captureSession = captureSession;
             this.captureDelayMs = captureDelayMs;
+            this.capturePreambleMs = capturePreambleMs;
             this.data = data;
         }
     }
 
     /**
-     *  Additional data conveyed by a {@link KeyPhraseRecognitionEvent}
+     *  A RecognitionConfig is provided to
+     *  {@link SoundTriggerModule#startRecognition(int, RecognitionConfig)} to configure the
+     *  recognition request.
+     */
+    public static class RecognitionConfig {
+        /** True if the DSP should capture the trigger sound and make it available for further
+         * capture. */
+        public final boolean captureRequested;
+        /** List of all keyphrases in the sound model for which recognition should be performed with
+         * options for each keyphrase. */
+        public final KeyphraseRecognitionExtra keyphrases[];
+        /** Opaque data for use by system applications who know about voice engine internals,
+         * typically during enrollment. */
+        public final byte[] data;
+
+        public RecognitionConfig(boolean captureRequested,
+                KeyphraseRecognitionExtra keyphrases[], byte[] data) {
+            this.captureRequested = captureRequested;
+            this.keyphrases = keyphrases;
+            this.data = data;
+        }
+    }
+
+    /**
+     * Confidence level for users defined in a keyphrase.
+     * - The confidence level is expressed in percent (0% -100%).
+     * When used in a {@link KeyphraseRecognitionEvent} it indicates the detected confidence level
+     * When used in a {@link RecognitionConfig} it indicates the minimum confidence level that
+     * should trigger a recognition.
+     * - The user ID is derived from the system ID {@link android.os.UserHandle#getIdentifier()}.
+     */
+    public static class ConfidenceLevel {
+        public final int userId;
+        public final int confidenceLevel;
+
+        public ConfidenceLevel(int userId, int confidenceLevel) {
+            this.userId = userId;
+            this.confidenceLevel = confidenceLevel;
+        }
+    }
+
+    /**
+     *  Additional data conveyed by a {@link KeyphraseRecognitionEvent}
      *  for a key phrase detection.
      */
-    public static class KeyPhraseRecognitionExtra {
-        /** Confidence level for each user defined in the key phrase in the same order as
-         * users in the key phrase. The confidence level is expressed in percentage (0% -100%) */
-        public final int[] confidenceLevels;
+    public static class KeyphraseRecognitionExtra {
+        /** The keyphrse ID */
+        public final int id;
 
         /** Recognition modes matched for this event */
         public final int recognitionModes;
 
-        KeyPhraseRecognitionExtra(int[] confidenceLevels, int recognitionModes) {
-            this.confidenceLevels = confidenceLevels;
+        /** Confidence levels for all users recognized (KeyphraseRecognitionEvent) or to
+         * be recognized (RecognitionConfig) */
+        public final ConfidenceLevel[] confidenceLevels;
+
+        public KeyphraseRecognitionExtra(int id, int recognitionModes,
+                                  ConfidenceLevel[] confidenceLevels) {
+            this.id = id;
             this.recognitionModes = recognitionModes;
+            this.confidenceLevels = confidenceLevels;
         }
     }
 
     /**
      *  Specialized {@link RecognitionEvent} for a key phrase detection.
      */
-    public static class KeyPhraseRecognitionEvent extends RecognitionEvent {
+    public static class KeyphraseRecognitionEvent extends RecognitionEvent {
         /** Indicates if the key phrase is present in the buffered audio available for capture */
-        public final KeyPhraseRecognitionExtra[] keyPhraseExtras;
+        public final KeyphraseRecognitionExtra[] keyphraseExtras;
 
         /** Additional data available for each recognized key phrases in the model */
-        public final boolean keyPhraseInCapture;
+        public final boolean keyphraseInCapture;
 
-        KeyPhraseRecognitionEvent(int status, int soundModelHandle, boolean captureAvailable,
-               int captureSession, int captureDelayMs, byte[] data,
-               boolean keyPhraseInCapture, KeyPhraseRecognitionExtra[] keyPhraseExtras) {
-            super(status, soundModelHandle, captureAvailable, captureSession, captureDelayMs, data);
-            this.keyPhraseInCapture = keyPhraseInCapture;
-            this.keyPhraseExtras = keyPhraseExtras;
+        KeyphraseRecognitionEvent(int status, int soundModelHandle, boolean captureAvailable,
+               int captureSession, int captureDelayMs, int capturePreambleMs, byte[] data,
+               boolean keyphraseInCapture, KeyphraseRecognitionExtra[] keyphraseExtras) {
+            super(status, soundModelHandle, captureAvailable, captureSession, captureDelayMs,
+                  capturePreambleMs, data);
+            this.keyphraseInCapture = keyphraseInCapture;
+            this.keyphraseExtras = keyphraseExtras;
         }
     }
 
diff --git a/core/java/android/hardware/soundtrigger/SoundTriggerModule.java b/core/java/android/hardware/soundtrigger/SoundTriggerModule.java
index 776f85d..4a54fd8 100644
--- a/core/java/android/hardware/soundtrigger/SoundTriggerModule.java
+++ b/core/java/android/hardware/soundtrigger/SoundTriggerModule.java
@@ -94,7 +94,8 @@
      * Recognition must be restarted after each callback (success or failure) received on
      * the {@link SoundTrigger.StatusListener}.
      * @param soundModelHandle The sound model handle to start listening to
-     * @param data Opaque data for use by the implementation for this recognition
+     * @param config contains configuration information for this recognition request:
+     *  recognition mode, keyphrases, users, minimum confidence levels...
      * @return - {@link SoundTrigger#STATUS_OK} in case of success
      *         - {@link SoundTrigger#STATUS_ERROR} in case of unspecified error
      *         - {@link SoundTrigger#STATUS_PERMISSION_DENIED} if the caller does not have
@@ -105,7 +106,7 @@
      *         service fails
      *         - {@link SoundTrigger#STATUS_INVALID_OPERATION} if the call is out of sequence
      */
-    public native int startRecognition(int soundModelHandle, byte[] data);
+    public native int startRecognition(int soundModelHandle, SoundTrigger.RecognitionConfig config);
 
     /**
      * Stop listening to all key phrases in a {@link SoundTrigger.SoundModel}
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 0297c84..fb4912f 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -491,11 +491,17 @@
      */
     public static final int TYPE_PROXY = 16;
 
-    /** {@hide} */
-    public static final int MAX_RADIO_TYPE   = TYPE_PROXY;
+    /**
+     * A virtual network using one or more native bearers.
+     * It may or may not be providing security services.
+     */
+    public static final int TYPE_VPN = 17;
 
     /** {@hide} */
-    public static final int MAX_NETWORK_TYPE = TYPE_PROXY;
+    public static final int MAX_RADIO_TYPE   = TYPE_VPN;
+
+    /** {@hide} */
+    public static final int MAX_NETWORK_TYPE = TYPE_VPN;
 
     /**
      * If you want to set the default network preference,you can directly
diff --git a/core/java/android/net/DummyDataStateTracker.java b/core/java/android/net/DummyDataStateTracker.java
deleted file mode 100644
index eff9f9f..0000000
--- a/core/java/android/net/DummyDataStateTracker.java
+++ /dev/null
@@ -1,219 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net;
-
-import android.content.Context;
-import android.os.Handler;
-import android.os.Message;
-import android.os.Messenger;
-import android.util.Slog;
-
-/**
- * A dummy data state tracker for use when we don't have a real radio
- * connection.  useful when bringing up a board or when you have network
- * access through other means.
- *
- * {@hide}
- */
-public class DummyDataStateTracker extends BaseNetworkStateTracker {
-
-    private static final String TAG = "DummyDataStateTracker";
-    private static final boolean DBG = true;
-    private static final boolean VDBG = false;
-
-    private boolean mTeardownRequested = false;
-    private Handler mTarget;
-    private boolean mPrivateDnsRouteSet = false;
-    private boolean mDefaultRouteSet = false;
-
-    // DEFAULT and HIPRI are the same connection.  If we're one of these we need to check if
-    // the other is also disconnected before we reset sockets
-    private boolean mIsDefaultOrHipri = false;
-
-    /**
-     * Create a new DummyDataStateTracker
-     * @param netType the ConnectivityManager network type
-     * @param tag the name of this network
-     */
-    public DummyDataStateTracker(int netType, String tag) {
-        mNetworkInfo = new NetworkInfo(netType);
-    }
-
-    /**
-     * Begin monitoring data connectivity.
-     *
-     * @param context is the current Android context
-     * @param target is the Handler to which to return the events.
-     */
-    public void startMonitoring(Context context, Handler target) {
-        mTarget = target;
-        mContext = context;
-    }
-
-    public boolean isPrivateDnsRouteSet() {
-        return mPrivateDnsRouteSet;
-    }
-
-    public void privateDnsRouteSet(boolean enabled) {
-        mPrivateDnsRouteSet = enabled;
-    }
-
-    public NetworkInfo getNetworkInfo() {
-        return mNetworkInfo;
-    }
-
-    public boolean isDefaultRouteSet() {
-        return mDefaultRouteSet;
-    }
-
-    public void defaultRouteSet(boolean enabled) {
-        mDefaultRouteSet = enabled;
-    }
-
-    /**
-     * This is not implemented.
-     */
-    public void releaseWakeLock() {
-    }
-
-    /**
-     * Report whether data connectivity is possible.
-     */
-    public boolean isAvailable() {
-        return true;
-    }
-
-    /**
-     * Return the system properties name associated with the tcp buffer sizes
-     * for this network.
-     */
-    public String getTcpBufferSizesPropName() {
-        return "net.tcp.buffersize.unknown";
-    }
-
-    /**
-     * Tear down mobile data connectivity, i.e., disable the ability to create
-     * mobile data connections.
-     * TODO - make async and return nothing?
-     */
-    public boolean teardown() {
-        setDetailedState(NetworkInfo.DetailedState.DISCONNECTING, "disabled", null);
-        setDetailedState(NetworkInfo.DetailedState.DISCONNECTED, "disabled", null);
-        return true;
-    }
-
-    @Override
-    public void captivePortalCheckCompleted(boolean isCaptivePortal) {
-        // not implemented
-    }
-
-    /**
-     * Record the detailed state of a network, and if it is a
-     * change from the previous state, send a notification to
-     * any listeners.
-     * @param state the new {@code DetailedState}
-     * @param reason a {@code String} indicating a reason for the state change,
-     * if one was supplied. May be {@code null}.
-     * @param extraInfo optional {@code String} providing extra information about the state change
-     */
-    private void setDetailedState(NetworkInfo.DetailedState state, String reason,
-            String extraInfo) {
-        if (DBG) log("setDetailed state, old ="
-                + mNetworkInfo.getDetailedState() + " and new state=" + state);
-        mNetworkInfo.setDetailedState(state, reason, extraInfo);
-        Message msg = mTarget.obtainMessage(EVENT_STATE_CHANGED, mNetworkInfo);
-        msg.sendToTarget();
-    }
-
-    public void setTeardownRequested(boolean isRequested) {
-        mTeardownRequested = isRequested;
-    }
-
-    public boolean isTeardownRequested() {
-        return mTeardownRequested;
-    }
-
-    /**
-     * Re-enable mobile data connectivity after a {@link #teardown()}.
-     * TODO - make async and always get a notification?
-     */
-    public boolean reconnect() {
-        setDetailedState(NetworkInfo.DetailedState.CONNECTING, "enabled", null);
-        setDetailedState(NetworkInfo.DetailedState.CONNECTED, "enabled", null);
-        setTeardownRequested(false);
-        return true;
-    }
-
-    /**
-     * Turn on or off the mobile radio. No connectivity will be possible while the
-     * radio is off. The operation is a no-op if the radio is already in the desired state.
-     * @param turnOn {@code true} if the radio should be turned on, {@code false} if
-     */
-    public boolean setRadio(boolean turnOn) {
-        return true;
-    }
-
-    @Override
-    public void setUserDataEnable(boolean enabled) {
-        // ignored
-    }
-
-    @Override
-    public void setPolicyDataEnable(boolean enabled) {
-        // ignored
-    }
-
-    @Override
-    public String toString() {
-        StringBuffer sb = new StringBuffer("Dummy data state: none, dummy!");
-        return sb.toString();
-    }
-
-    /**
-     * @see android.net.NetworkStateTracker#getLinkProperties()
-     */
-    public LinkProperties getLinkProperties() {
-        return new LinkProperties(mLinkProperties);
-    }
-
-    public void setDependencyMet(boolean met) {
-        // not supported on this network
-    }
-
-    @Override
-    public void addStackedLink(LinkProperties link) {
-        mLinkProperties.addStackedLink(link);
-    }
-
-    @Override
-    public void removeStackedLink(LinkProperties link) {
-        mLinkProperties.removeStackedLink(link);
-    }
-
-    @Override
-    public void supplyMessenger(Messenger messenger) {
-        // not supported on this network
-    }
-
-    static private void log(String s) {
-        Slog.d(TAG, s);
-    }
-
-    static private void loge(String s) {
-        Slog.e(TAG, s);
-    }
-}
diff --git a/core/java/android/text/TextUtils.java b/core/java/android/text/TextUtils.java
index 48122d6..1d1aafb 100644
--- a/core/java/android/text/TextUtils.java
+++ b/core/java/android/text/TextUtils.java
@@ -43,6 +43,7 @@
 import android.text.style.SuggestionSpan;
 import android.text.style.SuperscriptSpan;
 import android.text.style.TextAppearanceSpan;
+import android.text.style.TtsSpan;
 import android.text.style.TypefaceSpan;
 import android.text.style.URLSpan;
 import android.text.style.UnderlineSpan;
@@ -607,7 +608,9 @@
     /** @hide */
     public static final int LOCALE_SPAN = 23;
     /** @hide */
-    public static final int LAST_SPAN = LOCALE_SPAN;
+    public static final int TTS_SPAN = 24;
+    /** @hide */
+    public static final int LAST_SPAN = TTS_SPAN;
 
     /**
      * Flatten a CharSequence and whatever styles can be copied across processes
@@ -786,6 +789,10 @@
                     readSpan(p, sp, new LocaleSpan(p));
                     break;
 
+                case TTS_SPAN:
+                    readSpan(p, sp, new TtsSpan(p));
+                    break;
+
                 default:
                     throw new RuntimeException("bogus span encoding " + kind);
                 }
diff --git a/core/java/android/text/style/TtsSpan.java b/core/java/android/text/style/TtsSpan.java
new file mode 100644
index 0000000..ab736c5
--- /dev/null
+++ b/core/java/android/text/style/TtsSpan.java
@@ -0,0 +1,150 @@
+/*
+ * 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.text.style;
+
+import android.os.Parcel;
+import android.os.PersistableBundle;
+import android.text.ParcelableSpan;
+import android.text.TextUtils;
+
+/**
+ * A span that supplies addition meta-data intended for text-to-speech rendering
+ * of the associated text.  If the text is being processed by a text-to-speech
+ * engine, the engine may use the data in this span in addition to or instead of
+ * its associated text.
+ */
+public class TtsSpan implements ParcelableSpan {
+    private final String mType;
+    private final PersistableBundle mArgs;
+
+    /**
+     * This span type can be used to add morphosyntactic features to the text it
+     * spans over, or synthesize a something else than the spanned text.  Use
+     * the argument {@link #ARG_TEXT} to set a different text.
+     * Accepts the arguments {@link TtsSpan#ARG_GENDER},
+     * {@link TtsSpan#ARG_ANIMACY}, {@link TtsSpan#ARG_MULTIPLICITY} and
+     * {@link TtsSpan#ARG_CASE}.
+     */
+    public static final String TYPE_TEXT = "android.type.text";
+
+    /**
+     * The text associated with this span is a cardinal.  Must include the
+     * number to be synthesized with {@link #ARG_NUMBER}.
+     * Also accepts the arguments {@link TtsSpan#ARG_GENDER},
+     * {@link TtsSpan#ARG_ANIMACY}, {@link TtsSpan#ARG_MULTIPLICITY} and
+     * {@link TtsSpan#ARG_CASE}.
+     */
+    public static final String TYPE_CARDINAL = "android.type.cardinal";
+
+    /**
+     * String supplying the text to be synthesized.  The synthesizer is free
+     * to decide how to interpret the text.
+     */
+    public static final String ARG_TEXT = "android.arg.text";
+
+    /**
+     * Argument used to specify a whole number.  The value can be a string of
+     * digits of any size optionally prefixed with a - or +.
+     */
+    public static final String ARG_NUMBER = "android.arg.number";
+
+    /**
+     * String argument supplying gender information.  Can be any of
+     * {@link TtsSpan#GENDER_NEUTRAL}, {@link TtsSpan#GENDER_MALE} and
+     * {@link TtsSpan#GENDER_FEMALE}.
+     */
+    public static final String ARG_GENDER = "android.arg.gender";
+
+    public static final String GENDER_NEUTRAL = "android.neutral";
+    public static final String GENDER_MALE = "android.male";
+    public static final String GENDER_FEMALE = "android.female";
+
+    /**
+     * String argument supplying animacy information.  Can be
+     * {@link TtsSpan#ANIMACY_ANIMATE} or
+     * {@link TtsSpan#ANIMACY_INANIMATE}
+     */
+    public static final String ARG_ANIMACY = "android.arg.animacy";
+
+    public static final String ANIMACY_ANIMATE = "android.animate";
+    public static final String ANIMACY_INANIMATE = "android.inanimate";
+
+    /**
+     * String argument supplying multiplicity information.  Can be any of
+     * {@link TtsSpan#MULTIPLICITY_SINGLE},
+     * {@link TtsSpan#MULTIPLICITY_DUAL} and
+     * {@link TtsSpan#MULTIPLICITY_PLURAL}
+     */
+    public static final String ARG_MULTIPLICITY = "android.arg.multiplicity";
+
+    public static final String MULTIPLICITY_SINGLE = "android.single";
+    public static final String MULTIPLICITY_DUAL = "android.dual";
+    public static final String MULTIPLICITY_PLURAL = "android.plural";
+
+    /**
+     * String argument supplying case information.  Can be any of
+     * {@link TtsSpan#CASE_NOMINATIVE}, {@link TtsSpan#CASE_ACCUSATIVE},
+     * {@link TtsSpan#CASE_DATIVE}, {@link TtsSpan#CASE_ABLATIVE},
+     * {@link TtsSpan#CASE_GENITIVE}, {@link TtsSpan#CASE_VOCATIVE},
+     * {@link TtsSpan#CASE_LOCATIVE} and
+     * {@link TtsSpan#CASE_INSTRUMENTAL}
+     */
+    public static final String ARG_CASE = "android.arg.case";
+
+    public static final String CASE_NOMINATIVE = "android.nomative";
+    public static final String CASE_ACCUSATIVE = "android.accusative";
+    public static final String CASE_DATIVE = "android.dative";
+    public static final String CASE_ABLATIVE = "android.ablative";
+    public static final String CASE_GENITIVE = "android.genitive";
+    public static final String CASE_VOCATIVE = "android.vocative";
+    public static final String CASE_LOCATIVE = "android.locative";
+    public static final String CASE_INSTRUMENTAL = "android.instrumental";
+
+    public TtsSpan(String type, PersistableBundle args) {
+        mType = type;
+        mArgs = args;
+    }
+
+    public TtsSpan(Parcel src) {
+        mType = src.readString();
+        mArgs = src.readPersistableBundle();
+    }
+
+    public String getType() {
+        return mType;
+    }
+
+    public PersistableBundle getArgs() {
+        return mArgs;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeString(mType);
+        dest.writePersistableBundle(mArgs);
+    }
+
+    @Override
+    public int getSpanTypeId() {
+        return TextUtils.TTS_SPAN;
+    }
+}
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index fa4564e..57d1beb 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -127,8 +127,6 @@
     @Override
     void destroyHardwareResources(View view) {
         destroyResources(view);
-        // mRootNode belongs to us and not a view, so we need to destroy it
-        mRootNode.destroyDisplayListData();
         nDestroyHardwareResources(mNativeProxy);
     }
 
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 706fb1c..1e5f448 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -13625,6 +13625,10 @@
      * @hide
      */
     protected void destroyHardwareResources() {
+        // Although the Layer will be destroyed by RenderNode, we want to release
+        // the staging display list, which is also a signal to RenderNode that it's
+        // safe to free its copy of the display list as it knows that we will
+        // push an updated DisplayList if we try to draw again
         resetDisplayList();
     }
 
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index 5bd6f52..916586c 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -689,7 +689,7 @@
         try {
             getLockSettings().setLockPassword(password, userHandle);
             DevicePolicyManager dpm = getDevicePolicyManager();
-            if (password != null) {
+            if (!TextUtils.isEmpty(password)) {
                 int computedQuality = computePasswordQuality(password);
 
                 if (userHandle == UserHandle.USER_OWNER) {
@@ -764,9 +764,10 @@
                 }
                 setString(PASSWORD_HISTORY_KEY, passwordHistory, userHandle);
             } else {
+                // Empty password
                 if (userHandle == UserHandle.USER_OWNER) {
-                    // Update the encryption password.
-                    updateEncryptionPassword(StorageManager.CRYPT_TYPE_DEFAULT, password);
+                    // Set the encryption password to default.
+                    updateEncryptionPassword(StorageManager.CRYPT_TYPE_DEFAULT, null);
                 }
 
                 dpm.setActivePasswordState(
diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp
index f7886d3..53aca3d 100644
--- a/core/jni/android/graphics/Bitmap.cpp
+++ b/core/jni/android/graphics/Bitmap.cpp
@@ -316,7 +316,7 @@
 static jobject Bitmap_creator(JNIEnv* env, jobject, jintArray jColors,
                               jint offset, jint stride, jint width, jint height,
                               jint configHandle, jboolean isMutable) {
-    SkColorType colorType = SkBitmapConfigToColorType(static_cast<SkBitmap::Config>(configHandle));
+    SkColorType colorType = GraphicsJNI::legacyBitmapConfigToColorType(configHandle);
     if (NULL != jColors) {
         size_t n = env->GetArrayLength(jColors);
         if (n < SkAbs32(stride) * (size_t)height) {
@@ -350,11 +350,11 @@
 static jobject Bitmap_copy(JNIEnv* env, jobject, jlong srcHandle,
                            jint dstConfigHandle, jboolean isMutable) {
     const SkBitmap* src = reinterpret_cast<SkBitmap*>(srcHandle);
-    SkBitmap::Config dstConfig = static_cast<SkBitmap::Config>(dstConfigHandle);
+    SkColorType dstCT = GraphicsJNI::legacyBitmapConfigToColorType(dstConfigHandle);
     SkBitmap            result;
     JavaPixelAllocator  allocator(env);
 
-    if (!src->copyTo(&result, SkBitmapConfigToColorType(dstConfig), &allocator)) {
+    if (!src->copyTo(&result, dstCT, &allocator)) {
         return NULL;
     }
     return GraphicsJNI::createBitmap(env, new SkBitmap(result), allocator.getStorageObj(),
@@ -389,8 +389,7 @@
         jint width, jint height, jint configHandle, jint allocSize,
         jboolean requestPremul) {
     SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
-    SkBitmap::Config config = static_cast<SkBitmap::Config>(configHandle);
-    SkColorType colorType = SkBitmapConfigToColorType(config);
+    SkColorType colorType = GraphicsJNI::legacyBitmapConfigToColorType(configHandle);
 
     // ARGB_4444 is a deprecated format, convert automatically to 8888
     if (colorType == kARGB_4444_SkColorType) {
@@ -494,7 +493,7 @@
 
 static jint Bitmap_config(JNIEnv* env, jobject, jlong bitmapHandle) {
     SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
-    return static_cast<jint>(bitmap->config());
+    return GraphicsJNI::colorTypeToLegacyBitmapConfig(bitmap->colorType());
 }
 
 static jint Bitmap_getGenerationId(JNIEnv* env, jobject, jlong bitmapHandle) {
@@ -810,7 +809,7 @@
     const SkBitmap* bm1 = reinterpret_cast<SkBitmap*>(bm1Handle);
     if (bm0->width() != bm1->width() ||
         bm0->height() != bm1->height() ||
-        bm0->config() != bm1->config()) {
+        bm0->colorType() != bm1->colorType()) {
         return JNI_FALSE;
     }
 
@@ -822,7 +821,7 @@
         return JNI_FALSE;
     }
 
-    if (bm0->config() == SkBitmap::kIndex8_Config) {
+    if (bm0->colorType() == kIndex_8_SkColorType) {
         SkColorTable* ct0 = bm0->getColorTable();
         SkColorTable* ct1 = bm1->getColorTable();
         if (NULL == ct0 || NULL == ct1) {
diff --git a/core/jni/android/graphics/Graphics.cpp b/core/jni/android/graphics/Graphics.cpp
index 7a186a2..9177696 100644
--- a/core/jni/android/graphics/Graphics.cpp
+++ b/core/jni/android/graphics/Graphics.cpp
@@ -291,6 +291,54 @@
     env->SetFloatField(obj, gPointF_yFieldID, SkScalarToFloat(r.fY));
 }
 
+// This enum must keep these int values, to match the int values
+// in the java Bitmap.Config enum.
+enum LegacyBitmapConfig {
+    kNo_LegacyBitmapConfig          = 0,
+    kA8_LegacyBitmapConfig          = 1,
+    kIndex8_LegacyBitmapConfig      = 2,
+    kRGB_565_LegacyBitmapConfig     = 3,
+    kARGB_4444_LegacyBitmapConfig   = 4,
+    kARGB_8888_LegacyBitmapConfig   = 5,
+
+    kLastEnum_LegacyBitmapConfig = kARGB_8888_LegacyBitmapConfig
+};
+
+jint GraphicsJNI::colorTypeToLegacyBitmapConfig(SkColorType colorType) {
+    switch (colorType) {
+        case kN32_SkColorType:
+            return kARGB_8888_LegacyBitmapConfig;
+        case kARGB_4444_SkColorType:
+            return kARGB_4444_LegacyBitmapConfig;
+        case kRGB_565_SkColorType:
+            return kRGB_565_LegacyBitmapConfig;
+        case kIndex_8_SkColorType:
+            return kIndex8_LegacyBitmapConfig;
+        case kAlpha_8_SkColorType:
+            return kA8_LegacyBitmapConfig;
+        case kUnknown_SkColorType:
+        default:
+            break;
+    }
+    return kNo_LegacyBitmapConfig;
+}
+
+SkColorType GraphicsJNI::legacyBitmapConfigToColorType(jint legacyConfig) {
+    const uint8_t gConfig2ColorType[] = {
+        kUnknown_SkColorType,
+        kAlpha_8_SkColorType,
+        kIndex_8_SkColorType,
+        kRGB_565_SkColorType,
+        kARGB_4444_SkColorType,
+        kN32_SkColorType
+    };
+
+    if (legacyConfig < 0 || legacyConfig > kLastEnum_LegacyBitmapConfig) {
+        legacyConfig = kNo_LegacyBitmapConfig;
+    }
+    return static_cast<SkColorType>(gConfig2ColorType[legacyConfig]);
+}
+
 SkBitmap* GraphicsJNI::getNativeBitmap(JNIEnv* env, jobject bitmap) {
     SkASSERT(env);
     SkASSERT(bitmap);
@@ -308,10 +356,7 @@
     }
     SkASSERT(env->IsInstanceOf(jconfig, gBitmapConfig_class));
     int c = env->GetIntField(jconfig, gBitmapConfig_nativeInstanceID);
-    if (c < 0 || c >= SkBitmap::kConfigCount) {
-        c = kUnknown_SkColorType;
-    }
-    return SkBitmapConfigToColorType(static_cast<SkBitmap::Config>(c));
+    return legacyBitmapConfigToColorType(c);
 }
 
 SkCanvas* GraphicsJNI::getNativeCanvas(JNIEnv* env, jobject canvas) {
diff --git a/core/jni/android/graphics/GraphicsJNI.h b/core/jni/android/graphics/GraphicsJNI.h
index 6d82ceb..a03391d 100644
--- a/core/jni/android/graphics/GraphicsJNI.h
+++ b/core/jni/android/graphics/GraphicsJNI.h
@@ -58,6 +58,14 @@
     // ref to its SkRasterizer* (or NULL).
     static SkRasterizer* refNativeRasterizer(jlong rasterizerHandle);
 
+    /*
+     *  LegacyBitmapConfig is the old enum in Skia that matched the enum int values
+     *  in Bitmap.Config. Skia no longer supports this config, but has replaced it
+     *  with SkColorType. These routines convert between the two.
+     */
+    static SkColorType legacyBitmapConfigToColorType(jint legacyConfig);
+    static jint colorTypeToLegacyBitmapConfig(SkColorType colorType);
+
     /** Return the corresponding native colorType from the java Config enum,
         or kUnknown_SkColorType if the java object is null.
     */
diff --git a/core/jni/android/graphics/NinePatchImpl.cpp b/core/jni/android/graphics/NinePatchImpl.cpp
index 1793208..c162c48 100644
--- a/core/jni/android/graphics/NinePatchImpl.cpp
+++ b/core/jni/android/graphics/NinePatchImpl.cpp
@@ -38,18 +38,18 @@
 #include <utils/Log.h>
 
 static bool getColor(const SkBitmap& bitmap, int x, int y, SkColor* c) {
-    switch (bitmap.config()) {
-        case SkBitmap::kARGB_8888_Config:
+    switch (bitmap.colorType()) {
+        case kN32_SkColorType:
             *c = SkUnPreMultiply::PMColorToColor(*bitmap.getAddr32(x, y));
             break;
-        case SkBitmap::kRGB_565_Config:
+        case kRGB_565_SkColorType:
             *c = SkPixel16ToPixel32(*bitmap.getAddr16(x, y));
             break;
-        case SkBitmap::kARGB_4444_Config:
+        case kARGB_4444_SkColorType:
             *c = SkUnPreMultiply::PMColorToColor(
                                 SkPixel4444ToPixel32(*bitmap.getAddr16(x, y)));
             break;
-        case SkBitmap::kIndex8_Config: {
+        case kIndex_8_SkColorType: {
             SkColorTable* ctable = bitmap.getColorTable();
             *c = SkUnPreMultiply::PMColorToColor(
                                             (*ctable)[*bitmap.getAddr8(x, y)]);
diff --git a/core/jni/android/opengl/util.cpp b/core/jni/android/opengl/util.cpp
index a91c622..89baef8 100644
--- a/core/jni/android/opengl/util.cpp
+++ b/core/jni/android/opengl/util.cpp
@@ -562,18 +562,18 @@
     setGLDebugLevel(level);
 }
 
-static int checkFormat(SkBitmap::Config config, int format, int type)
+static int checkFormat(SkColorType colorType, int format, int type)
 {
-    switch(config) {
-        case SkBitmap::kIndex8_Config:
+    switch(colorType) {
+        case kIndex_8_SkColorType:
             if (format == GL_PALETTE8_RGBA8_OES)
                 return 0;
-        case SkBitmap::kARGB_8888_Config:
-        case SkBitmap::kA8_Config:
+        case kN32_SkColorType:
+        case kAlpha_8_SkColorType:
             if (type == GL_UNSIGNED_BYTE)
                 return 0;
-        case SkBitmap::kARGB_4444_Config:
-        case SkBitmap::kRGB_565_Config:
+        case kARGB_4444_SkColorType:
+        case kRGB_565_SkColorType:
             switch (type) {
                 case GL_UNSIGNED_SHORT_4_4_4_4:
                 case GL_UNSIGNED_SHORT_5_6_5:
@@ -590,36 +590,36 @@
     return -1;
 }
 
-static int getInternalFormat(SkBitmap::Config config)
+static int getInternalFormat(SkColorType colorType)
 {
-    switch(config) {
-        case SkBitmap::kA8_Config:
+    switch(colorType) {
+        case kAlpha_8_SkColorType:
             return GL_ALPHA;
-        case SkBitmap::kARGB_4444_Config:
+        case kARGB_4444_SkColorType:
             return GL_RGBA;
-        case SkBitmap::kARGB_8888_Config:
+        case kN32_SkColorType:
             return GL_RGBA;
-        case SkBitmap::kIndex8_Config:
+        case kIndex_8_SkColorType:
             return GL_PALETTE8_RGBA8_OES;
-        case SkBitmap::kRGB_565_Config:
+        case kRGB_565_SkColorType:
             return GL_RGB;
         default:
             return -1;
     }
 }
 
-static int getType(SkBitmap::Config config)
+static int getType(SkColorType colorType)
 {
-    switch(config) {
-        case SkBitmap::kA8_Config:
+    switch(colorType) {
+        case kAlpha_8_SkColorType:
             return GL_UNSIGNED_BYTE;
-        case SkBitmap::kARGB_4444_Config:
+        case kARGB_4444_SkColorType:
             return GL_UNSIGNED_SHORT_4_4_4_4;
-        case SkBitmap::kARGB_8888_Config:
+        case kN32_SkColorType:
             return GL_UNSIGNED_BYTE;
-        case SkBitmap::kIndex8_Config:
+        case kIndex_8_SkColorType:
             return -1; // No type for compressed data.
-        case SkBitmap::kRGB_565_Config:
+        case kRGB_565_SkColorType:
             return GL_UNSIGNED_SHORT_5_6_5;
         default:
             return -1;
@@ -631,9 +631,7 @@
 {
     SkBitmap const * nativeBitmap =
             (SkBitmap const *)env->GetLongField(jbitmap, nativeBitmapID);
-    const SkBitmap& bitmap(*nativeBitmap);
-    SkBitmap::Config config = bitmap.config();
-    return getInternalFormat(config);
+    return getInternalFormat(nativeBitmap->colorType());
 }
 
 static jint util_getType(JNIEnv *env, jclass clazz,
@@ -641,9 +639,7 @@
 {
     SkBitmap const * nativeBitmap =
             (SkBitmap const *)env->GetLongField(jbitmap, nativeBitmapID);
-    const SkBitmap& bitmap(*nativeBitmap);
-    SkBitmap::Config config = bitmap.config();
-    return getType(config);
+    return getType(nativeBitmap->colorType());
 }
 
 static jint util_texImage2D(JNIEnv *env, jclass clazz,
@@ -653,14 +649,14 @@
     SkBitmap const * nativeBitmap =
             (SkBitmap const *)env->GetLongField(jbitmap, nativeBitmapID);
     const SkBitmap& bitmap(*nativeBitmap);
-    SkBitmap::Config config = bitmap.config();
+    SkColorType colorType = bitmap.colorType();
     if (internalformat < 0) {
-        internalformat = getInternalFormat(config);
+        internalformat = getInternalFormat(colorType);
     }
     if (type < 0) {
-        type = getType(config);
+        type = getType(colorType);
     }
-    int err = checkFormat(config, internalformat, type);
+    int err = checkFormat(colorType, internalformat, type);
     if (err)
         return err;
     bitmap.lockPixels();
@@ -702,13 +698,13 @@
     SkBitmap const * nativeBitmap =
             (SkBitmap const *)env->GetLongField(jbitmap, nativeBitmapID);
     const SkBitmap& bitmap(*nativeBitmap);
-    SkBitmap::Config config = bitmap.config();
+    SkColorType colorType = bitmap.colorType();
     if (format < 0) {
-        format = getInternalFormat(config);
+        format = getInternalFormat(colorType);
         if (format == GL_PALETTE8_RGBA8_OES)
             return -1; // glCompressedTexSubImage2D() not supported
     }
-    int err = checkFormat(config, format, type);
+    int err = checkFormat(colorType, format, type);
     if (err)
         return err;
     bitmap.lockPixels();
diff --git a/core/jni/android_hardware_SoundTrigger.cpp b/core/jni/android_hardware_SoundTrigger.cpp
index 69e991d..c9a0b1e 100644
--- a/core/jni/android_hardware_SoundTrigger.cpp
+++ b/core/jni/android_hardware_SoundTrigger.cpp
@@ -37,6 +37,11 @@
     jmethodID    add;
 } gArrayListMethods;
 
+static jclass gUUIDClass;
+static struct {
+    jmethodID    toString;
+} gUUIDMethods;
+
 static const char* const kSoundTriggerClassPathName = "android/hardware/soundtrigger/SoundTrigger";
 static jclass gSoundTriggerClass;
 
@@ -57,41 +62,65 @@
                                      "android/hardware/soundtrigger/SoundTrigger$SoundModel";
 static jclass gSoundModelClass;
 static struct {
+    jfieldID    uuid;
     jfieldID    data;
 } gSoundModelFields;
 
-static const char* const kKeyPhraseClassPathName =
-                                     "android/hardware/soundtrigger/SoundTrigger$KeyPhrase";
-static jclass gKeyPhraseClass;
+static const char* const kKeyphraseClassPathName =
+                                     "android/hardware/soundtrigger/SoundTrigger$Keyphrase";
+static jclass gKeyphraseClass;
 static struct {
+    jfieldID id;
     jfieldID recognitionModes;
     jfieldID locale;
     jfieldID text;
-    jfieldID numUsers;
-} gKeyPhraseFields;
+    jfieldID users;
+} gKeyphraseFields;
 
-static const char* const kKeyPhraseSoundModelClassPathName =
-                                 "android/hardware/soundtrigger/SoundTrigger$KeyPhraseSoundModel";
-static jclass gKeyPhraseSoundModelClass;
+static const char* const kKeyphraseSoundModelClassPathName =
+                                 "android/hardware/soundtrigger/SoundTrigger$KeyphraseSoundModel";
+static jclass gKeyphraseSoundModelClass;
 static struct {
-    jfieldID    keyPhrases;
-} gKeyPhraseSoundModelFields;
+    jfieldID    keyphrases;
+} gKeyphraseSoundModelFields;
 
+static const char* const kRecognitionConfigClassPathName =
+                                     "android/hardware/soundtrigger/SoundTrigger$RecognitionConfig";
+static jclass gRecognitionConfigClass;
+static struct {
+    jfieldID captureRequested;
+    jfieldID keyphrases;
+    jfieldID data;
+} gRecognitionConfigFields;
 
 static const char* const kRecognitionEventClassPathName =
                                      "android/hardware/soundtrigger/SoundTrigger$RecognitionEvent";
 static jclass gRecognitionEventClass;
 static jmethodID   gRecognitionEventCstor;
 
-static const char* const kKeyPhraseRecognitionEventClassPathName =
-                             "android/hardware/soundtrigger/SoundTrigger$KeyPhraseRecognitionEvent";
-static jclass gKeyPhraseRecognitionEventClass;
-static jmethodID   gKeyPhraseRecognitionEventCstor;
+static const char* const kKeyphraseRecognitionEventClassPathName =
+                             "android/hardware/soundtrigger/SoundTrigger$KeyphraseRecognitionEvent";
+static jclass gKeyphraseRecognitionEventClass;
+static jmethodID   gKeyphraseRecognitionEventCstor;
 
-static const char* const kKeyPhraseRecognitionExtraClassPathName =
-                             "android/hardware/soundtrigger/SoundTrigger$KeyPhraseRecognitionExtra";
-static jclass gKeyPhraseRecognitionExtraClass;
-static jmethodID   gKeyPhraseRecognitionExtraCstor;
+static const char* const kKeyphraseRecognitionExtraClassPathName =
+                             "android/hardware/soundtrigger/SoundTrigger$KeyphraseRecognitionExtra";
+static jclass gKeyphraseRecognitionExtraClass;
+static jmethodID   gKeyphraseRecognitionExtraCstor;
+static struct {
+    jfieldID id;
+    jfieldID recognitionModes;
+    jfieldID confidenceLevels;
+} gKeyphraseRecognitionExtraFields;
+
+static const char* const kConfidenceLevelClassPathName =
+                             "android/hardware/soundtrigger/SoundTrigger$ConfidenceLevel";
+static jclass gConfidenceLevelClass;
+static jmethodID   gConfidenceLevelCstor;
+static struct {
+    jfieldID userId;
+    jfieldID confidenceLevel;
+} gConfidenceLevelFields;
 
 static Mutex gLock;
 
@@ -170,40 +199,51 @@
                 (struct sound_trigger_phrase_recognition_event *)event;
 
         jobjectArray jExtras = env->NewObjectArray(phraseEvent->num_phrases,
-                                                  gKeyPhraseRecognitionExtraClass, NULL);
+                                                  gKeyphraseRecognitionExtraClass, NULL);
         if (jExtras == NULL) {
             return;
         }
 
         for (size_t i = 0; i < phraseEvent->num_phrases; i++) {
-            jintArray jConfidenceLevels = env->NewIntArray(phraseEvent->phrase_extras[i].num_users);
+            jobjectArray jConfidenceLevels = env->NewObjectArray(
+                                                        phraseEvent->phrase_extras[i].num_levels,
+                                                        gConfidenceLevelClass, NULL);
+
             if (jConfidenceLevels == NULL) {
                 return;
             }
-            jint *nConfidenceLevels = env->GetIntArrayElements(jConfidenceLevels, NULL);
-            memcpy(nConfidenceLevels,
-                   phraseEvent->phrase_extras[i].confidence_levels,
-                   phraseEvent->phrase_extras[i].num_users * sizeof(int));
-            env->ReleaseIntArrayElements(jConfidenceLevels, nConfidenceLevels, 0);
-            jobject jNewExtra = env->NewObject(gKeyPhraseRecognitionExtraClass,
-                                               gKeyPhraseRecognitionExtraCstor,
-                                               jConfidenceLevels,
-                                               phraseEvent->phrase_extras[i].recognition_modes);
+            for (size_t j = 0; j < phraseEvent->phrase_extras[i].num_levels; j++) {
+                jobject jConfidenceLevel = env->NewObject(gConfidenceLevelClass,
+                                                  gConfidenceLevelCstor,
+                                                  phraseEvent->phrase_extras[i].levels[j].user_id,
+                                                  phraseEvent->phrase_extras[i].levels[j].level);
+                env->SetObjectArrayElement(jConfidenceLevels, j, jConfidenceLevel);
+                env->DeleteLocalRef(jConfidenceLevel);
+            }
+
+            jobject jNewExtra = env->NewObject(gKeyphraseRecognitionExtraClass,
+                                               gKeyphraseRecognitionExtraCstor,
+                                               phraseEvent->phrase_extras[i].id,
+                                               phraseEvent->phrase_extras[i].recognition_modes,
+                                               jConfidenceLevels);
 
             if (jNewExtra == NULL) {
                 return;
             }
             env->SetObjectArrayElement(jExtras, i, jNewExtra);
-
+            env->DeleteLocalRef(jNewExtra);
+            env->DeleteLocalRef(jConfidenceLevels);
         }
-        jEvent = env->NewObject(gKeyPhraseRecognitionEventClass, gKeyPhraseRecognitionEventCstor,
+        jEvent = env->NewObject(gKeyphraseRecognitionEventClass, gKeyphraseRecognitionEventCstor,
                                 event->status, event->model, event->capture_available,
-                               event->capture_session, event->capture_delay_ms, jData,
+                               event->capture_session, event->capture_delay_ms,
+                               event->capture_preamble_ms, jData,
                                phraseEvent->key_phrase_in_capture, jExtras);
     } else {
         jEvent = env->NewObject(gRecognitionEventClass, gRecognitionEventCstor,
                                 event->status, event->model, event->capture_available,
-                                event->capture_session, event->capture_delay_ms, jData);
+                                event->capture_session, event->capture_delay_ms,
+                                event->capture_preamble_ms, jData);
     }
 
 
@@ -374,13 +414,16 @@
                                              jobject jSoundModel, jintArray jHandle)
 {
     jint status = SOUNDTRIGGER_STATUS_OK;
-    char *nData = NULL;
+    jbyte *nData = NULL;
     struct sound_trigger_sound_model *nSoundModel;
     jbyteArray jData;
     sp<MemoryDealer> memoryDealer;
     sp<IMemory> memory;
     size_t size;
     sound_model_handle_t handle;
+    jobject jUuid;
+    jstring jUuidString;
+    const char *nUuidString;
 
     ALOGV("loadSoundModel");
     sp<SoundTrigger> module = getSoundTrigger(env, thiz);
@@ -404,13 +447,22 @@
     }
     size_t offset;
     sound_trigger_sound_model_type_t type;
-    if (env->IsInstanceOf(jSoundModel, gKeyPhraseSoundModelClass)) {
+    if (env->IsInstanceOf(jSoundModel, gKeyphraseSoundModelClass)) {
         offset = sizeof(struct sound_trigger_phrase_sound_model);
         type = SOUND_MODEL_TYPE_KEYPHRASE;
     } else {
         offset = sizeof(struct sound_trigger_sound_model);
         type = SOUND_MODEL_TYPE_UNKNOWN;
     }
+
+    jUuid = env->GetObjectField(jSoundModel, gSoundModelFields.uuid);
+    jUuidString = (jstring)env->CallObjectMethod(jUuid, gUUIDMethods.toString);
+    nUuidString = env->GetStringUTFChars(jUuidString, NULL);
+    sound_trigger_uuid_t nUuid;
+    SoundTrigger::stringToGuid(nUuidString, &nUuid);
+    env->ReleaseStringUTFChars(jUuidString, nUuidString);
+    env->DeleteLocalRef(jUuidString);
+
     jData = (jbyteArray)env->GetObjectField(jSoundModel, gSoundModelFields.data);
     if (jData == NULL) {
         status = SOUNDTRIGGER_STATUS_BAD_VALUE;
@@ -418,7 +470,7 @@
     }
     size = env->GetArrayLength(jData);
 
-    nData = (char *)env->GetByteArrayElements(jData, NULL);
+    nData = env->GetByteArrayElements(jData, NULL);
     if (jData == NULL) {
         status = SOUNDTRIGGER_STATUS_ERROR;
         goto exit;
@@ -438,6 +490,7 @@
     nSoundModel = (struct sound_trigger_sound_model *)memory->pointer();
 
     nSoundModel->type = type;
+    nSoundModel->uuid = nUuid;
     nSoundModel->data_size = size;
     nSoundModel->data_offset = offset;
     memcpy((char *)nSoundModel + offset, nData, size);
@@ -446,7 +499,7 @@
                 (struct sound_trigger_phrase_sound_model *)nSoundModel;
 
         jobjectArray jPhrases =
-            (jobjectArray)env->GetObjectField(jSoundModel, gKeyPhraseSoundModelFields.keyPhrases);
+            (jobjectArray)env->GetObjectField(jSoundModel, gKeyphraseSoundModelFields.keyphrases);
         if (jPhrases == NULL) {
             status = SOUNDTRIGGER_STATUS_BAD_VALUE;
             goto exit;
@@ -457,16 +510,26 @@
         ALOGV("loadSoundModel numPhrases %d", numPhrases);
         for (size_t i = 0; i < numPhrases; i++) {
             jobject jPhrase = env->GetObjectArrayElement(jPhrases, i);
+            phraseModel->phrases[i].id =
+                                    env->GetIntField(jPhrase,gKeyphraseFields.id);
             phraseModel->phrases[i].recognition_mode =
-                                    env->GetIntField(jPhrase,gKeyPhraseFields.recognitionModes);
-            phraseModel->phrases[i].num_users =
-                                    env->GetIntField(jPhrase, gKeyPhraseFields.numUsers);
-            jstring jLocale = (jstring)env->GetObjectField(jPhrase, gKeyPhraseFields.locale);
+                                    env->GetIntField(jPhrase,gKeyphraseFields.recognitionModes);
+
+            jintArray jUsers = (jintArray)env->GetObjectField(jPhrase, gKeyphraseFields.users);
+            phraseModel->phrases[i].num_users = env->GetArrayLength(jUsers);
+            jint *nUsers = env->GetIntArrayElements(jUsers, NULL);
+            memcpy(phraseModel->phrases[i].users,
+                   nUsers,
+                   phraseModel->phrases[i].num_users * sizeof(int));
+            env->ReleaseIntArrayElements(jUsers, nUsers, 0);
+            env->DeleteLocalRef(jUsers);
+
+            jstring jLocale = (jstring)env->GetObjectField(jPhrase, gKeyphraseFields.locale);
             const char *nLocale = env->GetStringUTFChars(jLocale, NULL);
             strncpy(phraseModel->phrases[i].locale,
                     nLocale,
                     SOUND_TRIGGER_MAX_LOCALE_LEN);
-            jstring jText = (jstring)env->GetObjectField(jPhrase, gKeyPhraseFields.text);
+            jstring jText = (jstring)env->GetObjectField(jPhrase, gKeyphraseFields.text);
             const char *nText = env->GetStringUTFChars(jText, NULL);
             strncpy(phraseModel->phrases[i].text,
                     nText,
@@ -478,6 +541,7 @@
             env->DeleteLocalRef(jText);
             ALOGV("loadSoundModel phrases %d text %s locale %s",
                   i, phraseModel->phrases[i].text, phraseModel->phrases[i].locale);
+            env->DeleteLocalRef(jPhrase);
         }
         env->DeleteLocalRef(jPhrases);
     }
@@ -490,7 +554,7 @@
         env->ReleaseIntArrayElements(jHandle, nHandle, NULL);
     }
     if (nData != NULL) {
-        env->ReleaseByteArrayElements(jData, (jbyte *)nData, NULL);
+        env->ReleaseByteArrayElements(jData, nData, NULL);
     }
     return status;
 }
@@ -512,7 +576,7 @@
 
 static jint
 android_hardware_SoundTrigger_startRecognition(JNIEnv *env, jobject thiz,
-                                               jint jHandle, jbyteArray jData)
+                                               jint jHandle, jobject jConfig)
 {
     jint status = SOUNDTRIGGER_STATUS_OK;
     ALOGV("startRecognition");
@@ -520,30 +584,83 @@
     if (module == NULL) {
         return SOUNDTRIGGER_STATUS_ERROR;
     }
+
+    if (!env->IsInstanceOf(jConfig, gRecognitionConfigClass)) {
+        return SOUNDTRIGGER_STATUS_BAD_VALUE;
+    }
+
+    jbyteArray jData = (jbyteArray)env->GetObjectField(jConfig, gRecognitionConfigFields.data);
     jsize dataSize = 0;
-    char *nData = NULL;
-    sp<IMemory> memory;
+    jbyte *nData = NULL;
     if (jData != NULL) {
         dataSize = env->GetArrayLength(jData);
         if (dataSize == 0) {
             return SOUNDTRIGGER_STATUS_BAD_VALUE;
         }
-        nData = (char *)env->GetByteArrayElements(jData, NULL);
+        nData = env->GetByteArrayElements(jData, NULL);
         if (nData == NULL) {
             return SOUNDTRIGGER_STATUS_ERROR;
         }
-        sp<MemoryDealer> memoryDealer =
-                new MemoryDealer(dataSize, "SoundTrigge-JNI::StartRecognition");
-        if (memoryDealer == 0) {
-            return SOUNDTRIGGER_STATUS_ERROR;
-        }
-        memory = memoryDealer->allocate(dataSize);
-        if (memory == 0 || memory->pointer() == NULL) {
-            return SOUNDTRIGGER_STATUS_ERROR;
-        }
-        memcpy(memory->pointer(), nData, dataSize);
     }
 
+    size_t totalSize = sizeof(struct sound_trigger_recognition_config) + dataSize;
+    sp<MemoryDealer> memoryDealer =
+            new MemoryDealer(totalSize, "SoundTrigge-JNI::StartRecognition");
+    if (memoryDealer == 0) {
+        return SOUNDTRIGGER_STATUS_ERROR;
+    }
+    sp<IMemory> memory = memoryDealer->allocate(totalSize);
+    if (memory == 0 || memory->pointer() == NULL) {
+        return SOUNDTRIGGER_STATUS_ERROR;
+    }
+    if (dataSize != 0) {
+        memcpy((char *)memory->pointer() + sizeof(struct sound_trigger_recognition_config),
+                nData,
+                dataSize);
+        env->ReleaseByteArrayElements(jData, nData, 0);
+    }
+    env->DeleteLocalRef(jData);
+    struct sound_trigger_recognition_config *config =
+                                    (struct sound_trigger_recognition_config *)memory->pointer();
+    config->data_size = dataSize;
+    config->data_offset = sizeof(struct sound_trigger_recognition_config);
+    config->capture_requested = env->GetIntField(jConfig,
+                                                 gRecognitionConfigFields.captureRequested);
+
+    config->num_phrases = 0;
+    jobjectArray jPhrases =
+        (jobjectArray)env->GetObjectField(jConfig, gRecognitionConfigFields.keyphrases);
+    if (jPhrases != NULL) {
+        config->num_phrases = env->GetArrayLength(jPhrases);
+    }
+    ALOGV("startRecognition num phrases %d", config->num_phrases);
+    for (size_t i = 0; i < config->num_phrases; i++) {
+        jobject jPhrase = env->GetObjectArrayElement(jPhrases, i);
+        config->phrases[i].id = env->GetIntField(jPhrase,
+                                                gKeyphraseRecognitionExtraFields.id);
+        config->phrases[i].recognition_modes = env->GetIntField(jPhrase,
+                                                gKeyphraseRecognitionExtraFields.recognitionModes);
+        config->phrases[i].num_levels = 0;
+        jobjectArray jConfidenceLevels = (jobjectArray)env->GetObjectField(jPhrase,
+                                                gKeyphraseRecognitionExtraFields.confidenceLevels);
+        if (jConfidenceLevels != NULL) {
+            config->phrases[i].num_levels = env->GetArrayLength(jConfidenceLevels);
+        }
+        ALOGV("startRecognition phrase %d num_levels %d", i, config->phrases[i].num_levels);
+        for (size_t j = 0; j < config->phrases[i].num_levels; j++) {
+            jobject jConfidenceLevel = env->GetObjectArrayElement(jConfidenceLevels, j);
+            config->phrases[i].levels[j].user_id = env->GetIntField(jConfidenceLevel,
+                                                                    gConfidenceLevelFields.userId);
+            config->phrases[i].levels[j].level = env->GetIntField(jConfidenceLevel,
+                                                          gConfidenceLevelFields.confidenceLevel);
+            env->DeleteLocalRef(jConfidenceLevel);
+        }
+        ALOGV("startRecognition phrases %d", i);
+        env->DeleteLocalRef(jConfidenceLevels);
+        env->DeleteLocalRef(jPhrase);
+    }
+    env->DeleteLocalRef(jPhrases);
+
     status = module->startRecognition(jHandle, memory);
     return status;
 }
@@ -586,7 +703,7 @@
         "(I)I",
         (void *)android_hardware_SoundTrigger_unloadSoundModel},
     {"startRecognition",
-        "(I[B)I",
+        "(ILandroid/hardware/soundtrigger/SoundTrigger$RecognitionConfig;)I",
         (void *)android_hardware_SoundTrigger_startRecognition},
     {"stopRecognition",
         "(I)I",
@@ -599,6 +716,10 @@
     gArrayListClass = (jclass) env->NewGlobalRef(arrayListClass);
     gArrayListMethods.add = env->GetMethodID(arrayListClass, "add", "(Ljava/lang/Object;)Z");
 
+    jclass uuidClass = env->FindClass("java/util/UUID");
+    gUUIDClass = (jclass) env->NewGlobalRef(uuidClass);
+    gUUIDMethods.toString = env->GetMethodID(uuidClass, "toString", "()Ljava/lang/String;");
+
     jclass lClass = env->FindClass(kSoundTriggerClassPathName);
     gSoundTriggerClass = (jclass) env->NewGlobalRef(lClass);
 
@@ -617,37 +738,63 @@
 
     jclass soundModelClass = env->FindClass(kSoundModelClassPathName);
     gSoundModelClass = (jclass) env->NewGlobalRef(soundModelClass);
+    gSoundModelFields.uuid = env->GetFieldID(soundModelClass, "uuid", "Ljava/util/UUID;");
     gSoundModelFields.data = env->GetFieldID(soundModelClass, "data", "[B");
 
-    jclass keyPhraseClass = env->FindClass(kKeyPhraseClassPathName);
-    gKeyPhraseClass = (jclass) env->NewGlobalRef(keyPhraseClass);
-    gKeyPhraseFields.recognitionModes = env->GetFieldID(keyPhraseClass, "recognitionModes", "I");
-    gKeyPhraseFields.locale = env->GetFieldID(keyPhraseClass, "locale", "Ljava/lang/String;");
-    gKeyPhraseFields.text = env->GetFieldID(keyPhraseClass, "text", "Ljava/lang/String;");
-    gKeyPhraseFields.numUsers = env->GetFieldID(keyPhraseClass, "numUsers", "I");
+    jclass keyphraseClass = env->FindClass(kKeyphraseClassPathName);
+    gKeyphraseClass = (jclass) env->NewGlobalRef(keyphraseClass);
+    gKeyphraseFields.id = env->GetFieldID(keyphraseClass, "id", "I");
+    gKeyphraseFields.recognitionModes = env->GetFieldID(keyphraseClass, "recognitionModes", "I");
+    gKeyphraseFields.locale = env->GetFieldID(keyphraseClass, "locale", "Ljava/lang/String;");
+    gKeyphraseFields.text = env->GetFieldID(keyphraseClass, "text", "Ljava/lang/String;");
+    gKeyphraseFields.users = env->GetFieldID(keyphraseClass, "users", "[I");
 
-    jclass keyPhraseSoundModelClass = env->FindClass(kKeyPhraseSoundModelClassPathName);
-    gKeyPhraseSoundModelClass = (jclass) env->NewGlobalRef(keyPhraseSoundModelClass);
-    gKeyPhraseSoundModelFields.keyPhrases = env->GetFieldID(keyPhraseSoundModelClass,
-                                         "keyPhrases",
-                                         "[Landroid/hardware/soundtrigger/SoundTrigger$KeyPhrase;");
+    jclass keyphraseSoundModelClass = env->FindClass(kKeyphraseSoundModelClassPathName);
+    gKeyphraseSoundModelClass = (jclass) env->NewGlobalRef(keyphraseSoundModelClass);
+    gKeyphraseSoundModelFields.keyphrases = env->GetFieldID(keyphraseSoundModelClass,
+                                         "keyphrases",
+                                         "[Landroid/hardware/soundtrigger/SoundTrigger$Keyphrase;");
 
 
     jclass recognitionEventClass = env->FindClass(kRecognitionEventClassPathName);
     gRecognitionEventClass = (jclass) env->NewGlobalRef(recognitionEventClass);
     gRecognitionEventCstor = env->GetMethodID(recognitionEventClass, "<init>",
-                                              "(IIZII[B)V");
+                                              "(IIZIII[B)V");
 
-    jclass keyPhraseRecognitionEventClass = env->FindClass(kKeyPhraseRecognitionEventClassPathName);
-    gKeyPhraseRecognitionEventClass = (jclass) env->NewGlobalRef(keyPhraseRecognitionEventClass);
-    gKeyPhraseRecognitionEventCstor = env->GetMethodID(keyPhraseRecognitionEventClass, "<init>",
-              "(IIZII[BZ[Landroid/hardware/soundtrigger/SoundTrigger$KeyPhraseRecognitionExtra;)V");
+    jclass keyphraseRecognitionEventClass = env->FindClass(kKeyphraseRecognitionEventClassPathName);
+    gKeyphraseRecognitionEventClass = (jclass) env->NewGlobalRef(keyphraseRecognitionEventClass);
+    gKeyphraseRecognitionEventCstor = env->GetMethodID(keyphraseRecognitionEventClass, "<init>",
+              "(IIZIII[BZ[Landroid/hardware/soundtrigger/SoundTrigger$KeyphraseRecognitionExtra;)V");
 
 
-    jclass keyPhraseRecognitionExtraClass = env->FindClass(kKeyPhraseRecognitionExtraClassPathName);
-    gKeyPhraseRecognitionExtraClass = (jclass) env->NewGlobalRef(keyPhraseRecognitionExtraClass);
-    gKeyPhraseRecognitionExtraCstor = env->GetMethodID(keyPhraseRecognitionExtraClass, "<init>",
-                                              "([II)V");
+    jclass keyRecognitionConfigClass = env->FindClass(kRecognitionConfigClassPathName);
+    gRecognitionConfigClass = (jclass) env->NewGlobalRef(keyRecognitionConfigClass);
+    gRecognitionConfigFields.captureRequested = env->GetFieldID(keyRecognitionConfigClass,
+                                                              "captureRequested",
+                                                              "Z");
+    gRecognitionConfigFields.keyphrases = env->GetFieldID(keyRecognitionConfigClass,
+                        "keyphrases",
+                        "[Landroid/hardware/soundtrigger/SoundTrigger$KeyphraseRecognitionExtra;");
+    gRecognitionConfigFields.data = env->GetFieldID(keyRecognitionConfigClass,
+                                                              "data",
+                                                              "[B");
+
+    jclass keyphraseRecognitionExtraClass = env->FindClass(kKeyphraseRecognitionExtraClassPathName);
+    gKeyphraseRecognitionExtraClass = (jclass) env->NewGlobalRef(keyphraseRecognitionExtraClass);
+    gKeyphraseRecognitionExtraCstor = env->GetMethodID(keyphraseRecognitionExtraClass, "<init>",
+                           "(II[Landroid/hardware/soundtrigger/SoundTrigger$ConfidenceLevel;)V");
+    gKeyphraseRecognitionExtraFields.id = env->GetFieldID(gKeyphraseRecognitionExtraClass, "id", "I");
+    gKeyphraseRecognitionExtraFields.recognitionModes = env->GetFieldID(gKeyphraseRecognitionExtraClass, "recognitionModes", "I");
+    gKeyphraseRecognitionExtraFields.confidenceLevels = env->GetFieldID(gKeyphraseRecognitionExtraClass,
+                                             "confidenceLevels",
+                                             "[Landroid/hardware/soundtrigger/SoundTrigger$ConfidenceLevel;");
+
+    jclass confidenceLevelClass = env->FindClass(kConfidenceLevelClassPathName);
+    gConfidenceLevelClass = (jclass) env->NewGlobalRef(confidenceLevelClass);
+    gConfidenceLevelCstor = env->GetMethodID(confidenceLevelClass, "<init>", "(II)V");
+    gConfidenceLevelFields.userId = env->GetFieldID(confidenceLevelClass, "userId", "I");
+    gConfidenceLevelFields.confidenceLevel = env->GetFieldID(confidenceLevelClass,
+                                                             "confidenceLevel", "I");
 
     int status = AndroidRuntime::registerNativeMethods(env,
                 kSoundTriggerClassPathName, gMethods, NELEM(gMethods));
@@ -657,5 +804,6 @@
                 kModuleClassPathName, gModuleMethods, NELEM(gModuleMethods));
     }
 
+
     return status;
 }
diff --git a/core/jni/com_google_android_gles_jni_EGLImpl.cpp b/core/jni/com_google_android_gles_jni_EGLImpl.cpp
index 3d421d5..8ad2eea 100644
--- a/core/jni/com_google_android_gles_jni_EGLImpl.cpp
+++ b/core/jni/com_google_android_gles_jni_EGLImpl.cpp
@@ -257,13 +257,13 @@
     return reinterpret_cast<jlong>(sur);
 }
 
-static PixelFormat convertPixelFormat(SkBitmap::Config format)
+static PixelFormat convertPixelFormat(SkColorType format)
 {
     switch (format) {
-    case SkBitmap::kARGB_8888_Config:   return PIXEL_FORMAT_RGBA_8888;
-    case SkBitmap::kARGB_4444_Config:   return PIXEL_FORMAT_RGBA_4444;
-    case SkBitmap::kRGB_565_Config:     return PIXEL_FORMAT_RGB_565;
-    default:                            return PIXEL_FORMAT_NONE;
+    case kN32_SkColorType:         return PIXEL_FORMAT_RGBA_8888;
+    case kARGB_4444_SkColorType:   return PIXEL_FORMAT_RGBA_4444;
+    case kRGB_565_SkColorType:     return PIXEL_FORMAT_RGB_565;
+    default:                       return PIXEL_FORMAT_NONE;
     }
 }
 
@@ -297,7 +297,7 @@
     pixmap.width  = nativeBitmap->width();
     pixmap.height = nativeBitmap->height();
     pixmap.stride = nativeBitmap->rowBytes() / nativeBitmap->bytesPerPixel();
-    pixmap.format = convertPixelFormat(nativeBitmap->config());
+    pixmap.format = convertPixelFormat(nativeBitmap->colorType());
     pixmap.data   = (uint8_t*)ref->pixels();
 
     base = beginNativeAttribList(_env, attrib_list);
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index e675dc7..2d1cc59 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1068,14 +1068,6 @@
         android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
         android:protectionLevel="signature" />
 
-    <!-- Allows an application to communicate with a SIM card using logical
-         channels. -->
-    <permission android:name="android.permission.SIM_COMMUNICATION"
-        android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
-        android:label="@string/permlab_sim_communication"
-        android:description="@string/permdesc_sim_communication"
-        android:protectionLevel="dangerous" />
-
     <!-- @SystemApi Allows TvInputService to access underlying TV input hardware such as
          built-in tuners and HDMI-in's.
          @hide This should only be used by OEM's TvInputService's.
diff --git a/core/res/res/values-mcc214-mnc07/config.xml b/core/res/res/values-mcc214-mnc07/config.xml
new file mode 100644
index 0000000..ce7526c
--- /dev/null
+++ b/core/res/res/values-mcc214-mnc07/config.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2013, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You my 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.
+*/
+-->
+
+<!-- These resources are around just to allow their values to be customized
+     for different hardware and product builds. -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+    <!-- Array of ConnectivityManager.TYPE_xxxx values allowable for tethering -->
+    <!-- Common options are [1, 4] for TYPE_WIFI and TYPE_MOBILE_DUN or
+    <!== [0,1,5,7] for TYPE_MOBILE, TYPE_WIFI, TYPE_MOBILE_HIPRI and TYPE_BLUETOOTH -->
+    <integer-array translatable="false" name="config_tether_upstream_types">
+      <item>1</item>
+      <item>4</item>
+      <item>7</item>
+      <item>9</item>
+    </integer-array>
+
+    <!-- String containing the apn value for tethering.  May be overriden by secure settings
+         TETHER_DUN_APN.  Value is a comma separated series of strings:
+         "name,apn,proxy,port,username,password,server,mmsc,mmsproxy,mmsport,mcc,mnc,auth,type"
+         note that empty fields can be ommitted: "name,apn,,,,,,,,,310,260,,DUN" -->
+    <string translatable="false" name="config_tether_apndata">Conexion Compartida,movistar.es,,,MOVISTAR,MOVISTAR,,,,,214,07,1,DUN</string>
+
+</resources>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 7f8fe4d..f34fbc13 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -31,6 +31,7 @@
        <item><xliff:g id="id">nfc</xliff:g></item>
        <item><xliff:g id="id">tty</xliff:g></item>
        <item><xliff:g id="id">speakerphone</xliff:g></item>
+       <item><xliff:g id="id">zen</xliff:g></item>
        <item><xliff:g id="id">mute</xliff:g></item>
        <item><xliff:g id="id">volume</xliff:g></item>
        <item><xliff:g id="id">wifi</xliff:g></item>
diff --git a/docs/html/preview/api-overview.jd b/docs/html/preview/api-overview.jd
index 2e99194..16bc444 100644
--- a/docs/html/preview/api-overview.jd
+++ b/docs/html/preview/api-overview.jd
@@ -781,7 +781,7 @@
 $ rm /tmp/device_owner.xml
 $ echo "&lt;?xml version='1.0' encoding='utf-8' standalone='yes' ?&gt;"
 &gt;&gt; /tmp/device_owner.xml
-$ echo "&device-owner package=\"&lt;your_device_owner_package&gt;\"
+$ echo "&lt;device-owner package=\"&lt;your_device_owner_package&gt;\"
 name=\"*&lt;your_organization_name&gt;\" /&gt;" &gt;&gt; /tmp/device_owner.xml
 $ adb push /tmp/device_owner.xml /data/system/device_owner.xml
 $ adb reboot
diff --git a/docs/html/tools/support-library/index.jd b/docs/html/tools/support-library/index.jd
index e905285..68eca49 100644
--- a/docs/html/tools/support-library/index.jd
+++ b/docs/html/tools/support-library/index.jd
@@ -61,6 +61,33 @@
 <div class="toggle-content opened">
   <p><a href="#" onclick="return toggleContent(this)">
     <img src="{@docRoot}assets/images/triangle-opened.png" class="toggle-content-img" alt=""
+/>Android Support Library, revision 20</a> <em>(July 2014)</em>
+  </p>
+  <div class="toggle-content-toggleme">
+    <dl>
+      <dt>Changes for v4 support library:</dt>
+      <dd>
+        <ul>
+          <li>Added extended notification support for Android Wear in
+            {@link android.support.v4.app.NotificationCompat.WearableExtender}, which allows you
+            to specify wearable-specific features in your notifications.</li>
+          <li>Added {@link android.support.v4.app.NotificationCompat.Action.WearableExtender},
+            which allows actions to be added on wearable notifications.</li>
+          <li>Added {@link android.support.v4.app.NotificationManagerCompat}, which allows you
+            to issue notifications that properly support wearable features.</li>
+          <li>Added {@link android.support.v4.app.RemoteInput}, which allows a handheld device
+            to receive voice input from a notification that appears on a wearable device.</li>
+          <li>Improved the handling of touch feedback in
+            {@link android.support.v4.widget.SwipeRefreshLayout}.</li>
+        </ul>
+      </dd>
+    </dl>
+  </div>
+</div>
+
+<div class="toggle-content closed">
+  <p><a href="#" onclick="return toggleContent(this)">
+    <img src="{@docRoot}assets/images/triangle-closed.png" class="toggle-content-img" alt=""
 />Android Support Library, revision 19.1.0</a> <em>(March 2014)</em>
   </p>
   <div class="toggle-content-toggleme">
diff --git a/libs/hwui/DisplayListOp.h b/libs/hwui/DisplayListOp.h
index 901c69e..75d52b4 100644
--- a/libs/hwui/DisplayListOp.h
+++ b/libs/hwui/DisplayListOp.h
@@ -732,7 +732,7 @@
         deferInfo.mergeable = state.mMatrix.isSimple() && state.mMatrix.positiveScale() &&
                 !state.mClipSideFlags &&
                 OpenGLRenderer::getXfermodeDirect(mPaint) == SkXfermode::kSrcOver_Mode &&
-                (mBitmap->config() != SkBitmap::kA8_Config);
+                (mBitmap->colorType() != kAlpha_8_SkColorType);
     }
 
     const SkBitmap* bitmap() { return mBitmap; }
diff --git a/libs/hwui/LayerRenderer.cpp b/libs/hwui/LayerRenderer.cpp
index 873baf5..a92ef94 100644
--- a/libs/hwui/LayerRenderer.cpp
+++ b/libs/hwui/LayerRenderer.cpp
@@ -383,20 +383,20 @@
         GLenum error = GL_NO_ERROR;
         bool status = false;
 
-        switch (bitmap->config()) {
-            case SkBitmap::kA8_Config:
+        switch (bitmap->colorType()) {
+            case kAlpha_8_SkColorType:
                 format = GL_ALPHA;
                 type = GL_UNSIGNED_BYTE;
                 break;
-            case SkBitmap::kRGB_565_Config:
+            case kRGB_565_SkColorType:
                 format = GL_RGB;
                 type = GL_UNSIGNED_SHORT_5_6_5;
                 break;
-            case SkBitmap::kARGB_4444_Config:
+            case kARGB_4444_SkColorType:
                 format = GL_RGBA;
                 type = GL_UNSIGNED_SHORT_4_4_4_4;
                 break;
-            case SkBitmap::kARGB_8888_Config:
+            case kN32_SkColorType:
             default:
                 format = GL_RGBA;
                 type = GL_UNSIGNED_BYTE;
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 1bbcff1..c9f541b 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -1957,7 +1957,7 @@
 
     const float x = (int) floorf(bounds.left + 0.5f);
     const float y = (int) floorf(bounds.top + 0.5f);
-    if (CC_UNLIKELY(bitmap->config() == SkBitmap::kA8_Config)) {
+    if (CC_UNLIKELY(bitmap->colorType() == kAlpha_8_SkColorType)) {
         drawAlpha8TextureMesh(x, y, x + bounds.getWidth(), y + bounds.getHeight(),
                 texture->id, paint, &vertices[0].x, &vertices[0].u,
                 GL_TRIANGLES, bitmapCount * 6, true,
@@ -1986,7 +1986,7 @@
     if (!texture) return DrawGlInfo::kStatusDone;
     const AutoTexture autoCleanup(texture);
 
-    if (CC_UNLIKELY(bitmap->config() == SkBitmap::kA8_Config)) {
+    if (CC_UNLIKELY(bitmap->colorType() == kAlpha_8_SkColorType)) {
         drawAlphaBitmap(texture, left, top, paint);
     } else {
         drawTextureRect(left, top, right, bottom, texture, paint);
@@ -2014,7 +2014,7 @@
     // to the vertex shader. The save/restore is a bit overkill.
     save(SkCanvas::kMatrix_SaveFlag);
     concatMatrix(matrix);
-    if (CC_UNLIKELY(bitmap->config() == SkBitmap::kA8_Config)) {
+    if (CC_UNLIKELY(bitmap->colorType() == kAlpha_8_SkColorType)) {
         drawAlphaBitmap(texture, 0.0f, 0.0f, paint);
     } else {
         drawTextureRect(0.0f, 0.0f, bitmap->width(), bitmap->height(), texture, paint);
@@ -2037,7 +2037,7 @@
     Texture* texture = mCaches.textureCache.getTransient(bitmap);
     const AutoTexture autoCleanup(texture);
 
-    if (CC_UNLIKELY(bitmap->config() == SkBitmap::kA8_Config)) {
+    if (CC_UNLIKELY(bitmap->colorType() == kAlpha_8_SkColorType)) {
         drawAlphaBitmap(texture, left, top, paint);
     } else {
         drawTextureRect(left, top, right, bottom, texture, paint);
@@ -2232,7 +2232,7 @@
         dstBottom = srcBottom - srcTop;
     }
 
-    if (CC_UNLIKELY(bitmap->config() == SkBitmap::kA8_Config)) {
+    if (CC_UNLIKELY(bitmap->colorType() == kAlpha_8_SkColorType)) {
         drawAlpha8TextureMesh(dstLeft, dstTop, dstRight, dstBottom,
                 texture->id, paint,
                 &mMeshVertices[0].x, &mMeshVertices[0].u,
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index 3cf625f..fe03806 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -63,11 +63,12 @@
         , mDisplayListData(0)
         , mStagingDisplayListData(0)
         , mAnimatorManager(*this)
-        , mLayer(0) {
+        , mLayer(0)
+        , mParentCount(0) {
 }
 
 RenderNode::~RenderNode() {
-    delete mDisplayListData;
+    deleteDisplayListData();
     delete mStagingDisplayListData;
     LayerRenderer::destroyLayerDeferred(mLayer);
 }
@@ -196,28 +197,12 @@
 void RenderNode::prepareTreeImpl(TreeInfo& info) {
     info.damageAccumulator->pushTransform(this);
 
-    switch (info.mode) {
-    case TreeInfo::MODE_FULL:
+    if (info.mode == TreeInfo::MODE_FULL) {
         pushStagingPropertiesChanges(info);
-        mAnimatorManager.animate(info);
-        break;
-    case TreeInfo::MODE_MAYBE_DETACHING:
-        pushStagingPropertiesChanges(info);
-        break;
-    case TreeInfo::MODE_RT_ONLY:
-        mAnimatorManager.animate(info);
-        break;
-    case TreeInfo::MODE_DESTROY_RESOURCES:
-        // This will also release the hardware layer if we have one as
-        // isRenderable() will return false, thus causing pushLayerUpdate
-        // to recycle the hardware layer
-        LOG_ALWAYS_FATAL_IF(mStagingDisplayListData || (mDisplayListData && !mNeedsDisplayListDataSync),
-                "View.destroyHardwareResources wasn't called!");
-        break;
     }
-
+    mAnimatorManager.animate(info);
     prepareLayer(info);
-    if (info.mode == TreeInfo::MODE_FULL || info.mode == TreeInfo::MODE_DESTROY_RESOURCES) {
+    if (info.mode == TreeInfo::MODE_FULL) {
         pushStagingDisplayListChanges(info);
     }
     prepareSubTree(info, mDisplayListData);
@@ -259,21 +244,30 @@
 void RenderNode::pushStagingDisplayListChanges(TreeInfo& info) {
     if (mNeedsDisplayListDataSync) {
         mNeedsDisplayListDataSync = false;
-        // Do a push pass on the old tree to handle freeing DisplayListData
-        // that are no longer used
-        TreeInfo::TraversalMode mode = TreeInfo::MODE_MAYBE_DETACHING;
-        if (CC_UNLIKELY(info.mode == TreeInfo::MODE_DESTROY_RESOURCES)) {
-            mode = TreeInfo::MODE_DESTROY_RESOURCES;
+        // Make sure we inc first so that we don't fluctuate between 0 and 1,
+        // which would thrash the layer cache
+        if (mStagingDisplayListData) {
+            for (size_t i = 0; i < mStagingDisplayListData->children().size(); i++) {
+                mStagingDisplayListData->children()[i]->mRenderNode->incParentRefCount();
+            }
         }
-        TreeInfo oldTreeInfo(mode, info);
-        prepareSubTree(oldTreeInfo, mDisplayListData);
-        delete mDisplayListData;
+        deleteDisplayListData();
         mDisplayListData = mStagingDisplayListData;
-        mStagingDisplayListData = 0;
+        mStagingDisplayListData = NULL;
         damageSelf(info);
     }
 }
 
+void RenderNode::deleteDisplayListData() {
+    if (mDisplayListData) {
+        for (size_t i = 0; i < mDisplayListData->children().size(); i++) {
+            mDisplayListData->children()[i]->mRenderNode->decParentRefCount();
+        }
+    }
+    delete mDisplayListData;
+    mDisplayListData = NULL;
+}
+
 void RenderNode::prepareSubTree(TreeInfo& info, DisplayListData* subtree) {
     if (subtree) {
         TextureCache& cache = Caches::getInstance().textureCache;
@@ -296,6 +290,35 @@
     }
 }
 
+void RenderNode::destroyHardwareResources() {
+    if (mLayer) {
+        LayerRenderer::destroyLayer(mLayer);
+        mLayer = NULL;
+    }
+    if (mDisplayListData) {
+        for (size_t i = 0; i < mDisplayListData->children().size(); i++) {
+            mDisplayListData->children()[i]->mRenderNode->destroyHardwareResources();
+        }
+        if (mNeedsDisplayListDataSync) {
+            // Next prepare tree we are going to push a new display list, so we can
+            // drop our current one now
+            deleteDisplayListData();
+        }
+    }
+}
+
+void RenderNode::decParentRefCount() {
+    LOG_ALWAYS_FATAL_IF(!mParentCount, "already 0!");
+    mParentCount--;
+    if (!mParentCount) {
+        // If a child of ours is being attached to our parent then this will incorrectly
+        // destroy its hardware resources. However, this situation is highly unlikely
+        // and the failure is "just" that the layer is re-created, so this should
+        // be safe enough
+        destroyHardwareResources();
+    }
+}
+
 /*
  * For property operations, we pass a savecount of 0, since the operations aren't part of the
  * displaylist, and thus don't have to compensate for the record-time/playback-time discrepancy in
diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h
index 7d42b59..54fa143 100644
--- a/libs/hwui/RenderNode.h
+++ b/libs/hwui/RenderNode.h
@@ -168,6 +168,7 @@
     }
 
     ANDROID_API virtual void prepareTree(TreeInfo& info);
+    void destroyHardwareResources();
 
     // UI thread only!
     ANDROID_API void addAnimator(const sp<BaseRenderNodeAnimator>& animator);
@@ -248,6 +249,10 @@
     void applyLayerPropertiesToLayer(TreeInfo& info);
     void prepareLayer(TreeInfo& info);
     void pushLayerUpdate(TreeInfo& info);
+    void deleteDisplayListData();
+
+    void incParentRefCount() { mParentCount++; }
+    void decParentRefCount();
 
     String8 mName;
 
@@ -256,6 +261,7 @@
     RenderProperties mStagingProperties;
 
     bool mNeedsDisplayListDataSync;
+    // WARNING: Do not delete this directly, you must go through deleteDisplayListData()!
     DisplayListData* mDisplayListData;
     DisplayListData* mStagingDisplayListData;
 
@@ -272,6 +278,14 @@
 
     // for projection surfaces, contains a list of all children items
     Vector<DrawRenderNodeOp*> mProjectedNodes;
+
+    // How many references our parent(s) have to us. Typically this should alternate
+    // between 2 and 1 (when a staging push happens we inc first then dec)
+    // When this hits 0 we are no longer in the tree, so any hardware resources
+    // (specifically Layers) should be released.
+    // This is *NOT* thread-safe, and should therefore only be tracking
+    // mDisplayListData, not mStagingDisplayListData.
+    uint32_t mParentCount;
 }; // class RenderNode
 
 } /* namespace uirenderer */
diff --git a/libs/hwui/TextureCache.cpp b/libs/hwui/TextureCache.cpp
index 9212d0a..ec9e30a 100644
--- a/libs/hwui/TextureCache.cpp
+++ b/libs/hwui/TextureCache.cpp
@@ -285,20 +285,20 @@
 
     Caches::getInstance().bindTexture(texture->id);
 
-    switch (bitmap->config()) {
-    case SkBitmap::kA8_Config:
+    switch (bitmap->colorType()) {
+    case kAlpha_8_SkColorType:
         glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
         uploadToTexture(resize, GL_ALPHA, bitmap->rowBytesAsPixels(), bitmap->bytesPerPixel(),
                 texture->width, texture->height, GL_UNSIGNED_BYTE, bitmap->getPixels());
         texture->blend = true;
         break;
-    case SkBitmap::kRGB_565_Config:
+    case kRGB_565_SkColorType:
         glPixelStorei(GL_UNPACK_ALIGNMENT, bitmap->bytesPerPixel());
         uploadToTexture(resize, GL_RGB, bitmap->rowBytesAsPixels(), bitmap->bytesPerPixel(),
                 texture->width, texture->height, GL_UNSIGNED_SHORT_5_6_5, bitmap->getPixels());
         texture->blend = false;
         break;
-    case SkBitmap::kARGB_8888_Config:
+    case kN32_SkColorType:
         glPixelStorei(GL_UNPACK_ALIGNMENT, bitmap->bytesPerPixel());
         uploadToTexture(resize, GL_RGBA, bitmap->rowBytesAsPixels(), bitmap->bytesPerPixel(),
                 texture->width, texture->height, GL_UNSIGNED_BYTE, bitmap->getPixels());
@@ -306,14 +306,14 @@
         // decoding happened
         texture->blend = !bitmap->isOpaque();
         break;
-    case SkBitmap::kARGB_4444_Config:
-    case SkBitmap::kIndex8_Config:
+    case kARGB_4444_SkColorType:
+    case kIndex_8_SkColorType:
         glPixelStorei(GL_UNPACK_ALIGNMENT, bitmap->bytesPerPixel());
         uploadLoFiTexture(resize, bitmap, texture->width, texture->height);
         texture->blend = !bitmap->isOpaque();
         break;
     default:
-        ALOGW("Unsupported bitmap config: %d", bitmap->config());
+        ALOGW("Unsupported bitmap colorType: %d", bitmap->colorType());
         break;
     }
 
diff --git a/libs/hwui/TreeInfo.h b/libs/hwui/TreeInfo.h
index 9746ac53..de09755 100644
--- a/libs/hwui/TreeInfo.h
+++ b/libs/hwui/TreeInfo.h
@@ -58,15 +58,6 @@
         // animators, but potentially things like SurfaceTexture updates
         // could be handled by this as well if there are no listeners
         MODE_RT_ONLY,
-        // The subtree is being detached. Maybe. If the RenderNode is present
-        // in both the old and new display list's children then it will get a
-        // MODE_MAYBE_DETACHING followed shortly by a MODE_FULL.
-        // Push any pending display list changes in case it is detached,
-        // but don't evaluate animators and such as if it isn't detached as a
-        // MODE_FULL will follow shortly.
-        MODE_MAYBE_DETACHING,
-        // Destroy all hardware resources, including DisplayListData, in the tree.
-        MODE_DESTROY_RESOURCES,
     };
 
     explicit TreeInfo(TraversalMode mode, RenderState& renderState)
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index f5d4f8b..57279b7 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -250,8 +250,7 @@
     stopDrawing();
     if (mEglManager.hasEglContext()) {
         requireGlContext();
-        TreeInfo info(TreeInfo::MODE_DESTROY_RESOURCES, mRenderThread.renderState());
-        mRootRenderNode->prepareTree(info);
+        mRootRenderNode->destroyHardwareResources();
         Caches::getInstance().flush(Caches::kFlushMode_Layers);
     }
 }
diff --git a/media/java/android/media/tv/ITvInputClient.aidl b/media/java/android/media/tv/ITvInputClient.aidl
index 011da35..2854007 100644
--- a/media/java/android/media/tv/ITvInputClient.aidl
+++ b/media/java/android/media/tv/ITvInputClient.aidl
@@ -18,6 +18,7 @@
 
 import android.content.ComponentName;
 import android.media.tv.ITvInputSession;
+import android.net.Uri;
 import android.os.Bundle;
 import android.view.InputChannel;
 
@@ -34,4 +35,5 @@
     void onVideoStreamChanged(int width, int height, boolean interlaced, int seq);
     void onAudioStreamChanged(int channelCount, int seq);
     void onClosedCaptionStreamChanged(boolean hasClosedCaption, int seq);
+    void onChannelRetuned(in Uri channelUri, int seq);
 }
diff --git a/media/java/android/media/tv/ITvInputSessionCallback.aidl b/media/java/android/media/tv/ITvInputSessionCallback.aidl
index 00f2922a..5a57ccd 100644
--- a/media/java/android/media/tv/ITvInputSessionCallback.aidl
+++ b/media/java/android/media/tv/ITvInputSessionCallback.aidl
@@ -17,6 +17,7 @@
 package android.media.tv;
 
 import android.media.tv.ITvInputSession;
+import android.net.Uri;
 import android.os.Bundle;
 
 /**
@@ -30,4 +31,5 @@
     void onVideoStreamChanged(int width, int height, boolean interlaced);
     void onAudioStreamChanged(int channelCount);
     void onClosedCaptionStreamChanged(boolean hasClosedCaption);
+    void onChannelRetuned(in Uri channelUri);
 }
diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java
index daa7009..834ce64 100644
--- a/media/java/android/media/tv/TvInputManager.java
+++ b/media/java/android/media/tv/TvInputManager.java
@@ -125,6 +125,16 @@
         }
 
         /**
+         * This is called when the channel of this session is changed by the underlying TV input
+         * with out any {@link TvInputManager.Session#tune(Uri)} request.
+         *
+         * @param session A {@link TvInputManager.Session} associated with this callback
+         * @param channelUri The URI of a channel.
+         */
+        public void onChannelRetuned(Session session, Uri channelUri) {
+        }
+
+        /**
          * This is called when a custom event has been sent from this session.
          *
          * @param session A {@link TvInputManager.Session} associated with this callback
@@ -194,6 +204,15 @@
             });
         }
 
+        public void postChannelRetuned(final Uri channelUri) {
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    mSessionCallback.onChannelRetuned(mSession, channelUri);
+                }
+            });
+        }
+
         public void postSessionEvent(final String eventType, final Bundle eventArgs) {
             mHandler.post(new Runnable() {
                 @Override
@@ -318,6 +337,18 @@
             }
 
             @Override
+            public void onChannelRetuned(Uri channelUri, int seq) {
+                synchronized (mSessionCallbackRecordMap) {
+                    SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
+                    if (record == null) {
+                        Log.e(TAG, "Callback not found for seq " + seq);
+                        return;
+                    }
+                    record.postChannelRetuned(channelUri);
+                }
+            }
+
+            @Override
             public void onSessionEvent(String eventType, Bundle eventArgs, int seq) {
                 synchronized (mSessionCallbackRecordMap) {
                     SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
diff --git a/media/java/android/media/tv/TvInputService.java b/media/java/android/media/tv/TvInputService.java
index 0f4a930..1e512cd 100644
--- a/media/java/android/media/tv/TvInputService.java
+++ b/media/java/android/media/tv/TvInputService.java
@@ -286,6 +286,25 @@
         }
 
         /**
+         * Notifies the channel of the session is retuned by TV input.
+         *
+         * @param channelUri The URI of a channel.
+         */
+        public void dispatchChannelRetuned(final Uri channelUri) {
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    try {
+                        if (DEBUG) Log.d(TAG, "dispatchChannelRetuned");
+                        mSessionCallback.onChannelRetuned(channelUri);
+                    } catch (RemoteException e) {
+                        Log.w(TAG, "error in dispatchChannelRetuned");
+                    }
+                }
+            });
+        }
+
+        /**
          * Called when the session is released.
          */
         public abstract void onRelease();
diff --git a/media/java/android/media/tv/TvView.java b/media/java/android/media/tv/TvView.java
index a913e59c..664a215 100644
--- a/media/java/android/media/tv/TvView.java
+++ b/media/java/android/media/tv/TvView.java
@@ -456,6 +456,16 @@
         }
 
         /**
+         * This is invoked when the channel of this TvView is changed by the underlying TV input
+         * with out any {@link TvView#tune(String, Uri)} request.
+         *
+         * @param inputId The ID of the TV input bound to this view.
+         * @param channelUri The URI of a channel.
+         */
+        public void onChannelRetuned(String inputId, Uri channelUri) {
+        }
+
+        /**
          * This is invoked when a custom event from the bound TV input is sent to this view.
          *
          * @param eventType The type of the event.
@@ -562,6 +572,16 @@
         }
 
         @Override
+        public void onChannelRetuned(Session session, Uri channelUri) {
+            if (DEBUG) {
+                Log.d(TAG, "onChannelChangedByTvInput(" + channelUri + ")");
+            }
+            if (mListener != null) {
+                mListener.onChannelRetuned(mInputId, channelUri);
+            }
+        }
+
+        @Override
         public void onSessionEvent(TvInputManager.Session session, String eventType,
                 Bundle eventArgs) {
             if (mListener != null) {
diff --git a/native/graphics/jni/bitmap.cpp b/native/graphics/jni/bitmap.cpp
index eaa2cbe..df0751d 100644
--- a/native/graphics/jni/bitmap.cpp
+++ b/native/graphics/jni/bitmap.cpp
@@ -34,17 +34,17 @@
         info->stride    = bm->rowBytes();
         info->flags     = 0;
 
-        switch (bm->config()) {
-            case SkBitmap::kARGB_8888_Config:
+        switch (bm->colorType()) {
+            case kN32_SkColorType:
                 info->format = ANDROID_BITMAP_FORMAT_RGBA_8888;
                 break;
-            case SkBitmap::kRGB_565_Config:
+            case kRGB_565_SkColorType:
                 info->format = ANDROID_BITMAP_FORMAT_RGB_565;
                 break;
-            case SkBitmap::kARGB_4444_Config:
+            case kARGB_4444_SkColorType:
                 info->format = ANDROID_BITMAP_FORMAT_RGBA_4444;
                 break;
-            case SkBitmap::kA8_Config:
+            case kAlpha_8_SkColorType:
                 info->format = ANDROID_BITMAP_FORMAT_A_8;
                 break;
             default:
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 23a4722..b7210e1 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -53,7 +53,7 @@
     <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
     <uses-permission android:name="android.permission.MANAGE_NETWORK_POLICY" />
     <uses-permission android:name="android.permission.CONNECTIVITY_INTERNAL" />
-
+    <uses-permission android:name="android.permission.READ_NETWORK_USAGE_HISTORY" />
 
     <!-- Physical hardware -->
     <uses-permission android:name="android.permission.MANAGE_USB" />
diff --git a/packages/SystemUI/res/drawable/stat_sys_zen_none.xml b/packages/SystemUI/res/drawable/stat_sys_zen_none.xml
new file mode 100644
index 0000000..101e3c4
--- /dev/null
+++ b/packages/SystemUI/res/drawable/stat_sys_zen_none.xml
@@ -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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android" >
+    <size
+        android:width="19dp"
+        android:height="19dp"/>
+
+    <viewport
+        android:viewportWidth="48.0"
+        android:viewportHeight="48.0"/>
+
+    <path
+        android:fill="#FFFFFFFF"
+        android:pathData="M24.0,4.0C13.0,4.0 4.0,13.0 4.0,24.0c0.0,11.0 9.0,20.0 20.0,20.0c11.0,0.0 20.0,-9.0 20.0,-20.0C44.0,13.0 35.0,4.0 24.0,4.0zM24.0,40.0c-8.8,0.0 -16.0,-7.2 -16.0,-16.0c0.0,-3.7 1.3,-7.1 3.4,-9.8l22.4,22.4C31.1,38.7 27.7,40.0 24.0,40.0zM36.6,33.8L14.2,11.4C16.9,9.3 20.3,8.0 24.0,8.0c8.8,0.0 16.0,7.2 16.0,16.0C40.0,27.7 38.7,31.1 36.6,33.8z"/>
+</vector>
diff --git a/packages/SystemUI/res/layout/data_usage.xml b/packages/SystemUI/res/layout/data_usage.xml
new file mode 100644
index 0000000..63d22b2
--- /dev/null
+++ b/packages/SystemUI/res/layout/data_usage.xml
@@ -0,0 +1,82 @@
+<?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="wrap_content"
+    android:orientation="vertical" >
+
+    <TextView
+        android:id="@android:id/title"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textAppearance="@style/TextAppearance.QS.DataUsage" />
+
+    <TextView
+        android:id="@+id/usage_text"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textAppearance="@style/TextAppearance.QS.DataUsage.Usage" />
+
+    <com.android.systemui.qs.DataUsageGraph
+        android:id="@+id/usage_graph"
+        android:layout_width="match_parent"
+        android:layout_height="8dp"
+        android:layout_marginBottom="@dimen/qs_panel_padding"
+        android:layout_marginTop="8dp" />
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal" >
+
+        <TextView
+            android:id="@+id/usage_carrier_text"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:textAppearance="@style/TextAppearance.QS.DataUsage" />
+
+        <TextView
+            android:id="@+id/usage_info_top_text"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:textAppearance="@style/TextAppearance.QS.DataUsage" />
+    </LinearLayout>
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="4dp"
+        android:orientation="horizontal" >
+
+        <TextView
+            android:id="@+id/usage_period_text"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:textAppearance="@style/TextAppearance.QS.DataUsage.Secondary" />
+
+        <TextView
+            android:id="@+id/usage_info_bottom_text"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:textAppearance="@style/TextAppearance.QS.DataUsage.Secondary" />
+    </LinearLayout>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 69a22fc..65a6004 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -36,12 +36,14 @@
     <color name="qs_batterymeter_frame_color">#FF404040</color>
     <color name="system_primary_color">#ff263238</color>
     <color name="system_secondary_color">#ff384248</color>
-    <color name="system_accent_color">#ff7fcac3</color>
+    <color name="system_accent_color">#ff80CBC4</color><!-- deep teal 200 -->
     <color name="system_error_color">#fff0592b</color>
     <color name="qs_tile_divider">#29ffffff</color><!-- 16% white -->
     <color name="qs_tile_text">#B3FFFFFF</color><!-- 70% white -->
     <color name="qs_subhead">#66FFFFFF</color><!-- 40% white -->
-    <color name="status_bar_clock_color">#FFFFFFFF</color>
+    <color name="data_usage_secondary">#99FFFFFF</color><!-- 60% white -->
+    <color name="data_usage_graph_track">#33FFFFFF</color><!-- 20% white -->
+    <color name="status_bar_clock_color">#33FFFFFF</color>
 
     <!-- Tint color for the content on the notification overflow card. -->
     <color name="keyguard_overflow_content_color">#ff686868</color>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index e0d48bd..a6e5fea 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -546,6 +546,10 @@
     <string name="quick_settings_notifications_label">Notifications</string>
     <!-- QuickSettings: Flashlight [CHAR LIMIT=NONE] -->
     <string name="quick_settings_flashlight_label">Flashlight</string>
+    <!-- QuickSettings: Cellular detail panel title [CHAR LIMIT=NONE] -->
+    <string name="quick_settings_cellular_detail_title">Cellular data</string>
+    <!-- QuickSettings: Cellular detail panel, data usage title [CHAR LIMIT=NONE] -->
+    <string name="quick_settings_cellular_detail_data_usage">Data usage</string>
 
     <!-- Recents: The empty recents string. [CHAR LIMIT=NONE] -->
     <string name="recents_empty_message">No recent apps</string>
@@ -584,7 +588,7 @@
     <string name="description_direction_left">"Slide left for <xliff:g id="target_description" example="Unlock">%s</xliff:g>.</string>
 
     <!-- Zen mode: Alarm warning. [CHAR LIMIT=40] -->
-    <string name="zen_alarm_warning">You won\'t hear your alarms</string>
+    <string name="zen_alarm_warning">You won\'t hear alarms or timers</string>
 
     <!-- Zen mode: No interruptions. [CHAR LIMIT=40] -->
     <string name="zen_no_interruptions">No interruptions</string>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 3f68c31..9984bba6 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -169,7 +169,7 @@
 
     <style name="TextAppearance.QS.DetailItemSecondary">
         <item name="android:textSize">14sp</item>
-        <item name="android:textColor">#7fcac3</item>
+        <item name="android:textColor">@color/system_accent_color</item>
     </style>
 
     <style name="TextAppearance.QS.DetailButton">
@@ -196,6 +196,19 @@
         <item name="android:fontFamily">sans-serif-medium</item>
     </style>
 
+    <style name="TextAppearance.QS.DataUsage">
+        <item name="android:textSize">14sp</item>
+    </style>
+
+    <style name="TextAppearance.QS.DataUsage.Usage">
+        <item name="android:textSize">36sp</item>
+        <item name="android:textColor">@color/system_accent_color</item>
+    </style>
+
+    <style name="TextAppearance.QS.DataUsage.Secondary">
+        <item name="android:textColor">@color/data_usage_secondary</item>
+    </style>
+
     <style name="BaseBrightnessDialogContainer">
         <item name="android:layout_width">match_parent</item>
         <item name="android:layout_height">wrap_content</item>
diff --git a/packages/SystemUI/src/com/android/systemui/qs/DataUsageGraph.java b/packages/SystemUI/src/com/android/systemui/qs/DataUsageGraph.java
new file mode 100644
index 0000000..8b1c778
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/DataUsageGraph.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.RectF;
+import android.util.AttributeSet;
+import android.view.View;
+
+import com.android.systemui.R;
+
+public class DataUsageGraph extends View {
+
+    private final int mBackgroundColor;
+    private final int mUsageColor;
+    private final RectF mTmpRect = new RectF();
+    private final Paint mTmpPaint = new Paint();
+
+    private long mMaxLevel = 1;
+    private long mLimitLevel;
+    private long mWarningLevel;
+    private long mUsageLevel;
+
+    public DataUsageGraph(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        mBackgroundColor = context.getResources().getColor(R.color.data_usage_graph_track);
+        mUsageColor = context.getResources().getColor(R.color.system_accent_color);
+    }
+
+    public void setLevels(long maxLevel, long limitLevel, long warningLevel, long usageLevel) {
+        mMaxLevel = Math.max(maxLevel, 1);
+        mLimitLevel = limitLevel;
+        mWarningLevel = warningLevel;
+        mUsageLevel = usageLevel;
+        postInvalidate();
+    }
+
+    @Override
+    protected void onDraw(Canvas canvas) {
+        super.onDraw(canvas);
+
+        final RectF r = mTmpRect;
+        final Paint p = mTmpPaint;
+        final int w = getWidth();
+        final int h = getHeight();
+
+        // draw background
+        r.set(0, 0, w, h);
+        p.setColor(mBackgroundColor);
+        canvas.drawRect(r, p);
+
+        // draw usage
+        r.set(0, 0, w * mUsageLevel / (float) mMaxLevel, h);
+        p.setColor(mUsageColor);
+        canvas.drawRect(r, p);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
index 6d91d33..9048ee8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
@@ -20,24 +20,34 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.res.Resources;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
 
 import com.android.systemui.R;
+import com.android.systemui.qs.DataUsageGraph;
 import com.android.systemui.qs.QSTile;
 import com.android.systemui.qs.QSTileView;
 import com.android.systemui.qs.SignalTileView;
 import com.android.systemui.statusbar.policy.NetworkController;
+import com.android.systemui.statusbar.policy.NetworkController.DataUsageInfo;
 import com.android.systemui.statusbar.policy.NetworkController.NetworkSignalChangedCallback;
 
+import java.text.DecimalFormat;
+
 /** Quick settings tile: Cellular **/
 public class CellularTile extends QSTile<QSTile.SignalState> {
     private static final Intent CELLULAR_SETTINGS = new Intent().setComponent(new ComponentName(
             "com.android.settings", "com.android.settings.Settings$DataUsageSummaryActivity"));
 
     private final NetworkController mController;
+    private final CellularDetailAdapter mDetailAdapter;
 
     public CellularTile(Host host) {
         super(host);
         mController = host.getNetworkController();
+        mDetailAdapter = new CellularDetailAdapter();
     }
 
     @Override
@@ -46,6 +56,11 @@
     }
 
     @Override
+    public DetailAdapter getDetailAdapter() {
+        return mDetailAdapter;
+    }
+
+    @Override
     public void setListening(boolean listening) {
         if (listening) {
             mController.addNetworkSignalChangedCallback(mCallback);
@@ -61,7 +76,11 @@
 
     @Override
     protected void handleClick() {
-        mHost.startSettingsActivity(CELLULAR_SETTINGS);
+        if (mController.isMobileDataSupported()) {
+            showDetail(true);
+        } else {
+            mHost.startSettingsActivity(CELLULAR_SETTINGS);
+        }
     }
 
     @Override
@@ -157,5 +176,81 @@
         public void onAirplaneModeChanged(boolean enabled) {
             // noop
         }
+
+        public void onMobileDataEnabled(boolean enabled) {
+            mDetailAdapter.setMobileDataEnabled(enabled);
+        }
     };
+
+    private final class CellularDetailAdapter implements DetailAdapter {
+        private static final double KB = 1024;
+        private static final double MB = 1024 * KB;
+        private static final double GB = 1024 * MB;
+
+        private final DecimalFormat FORMAT = new DecimalFormat("#.##");
+
+        @Override
+        public int getTitle() {
+            return R.string.quick_settings_cellular_detail_title;
+        }
+
+        @Override
+        public Boolean getToggleState() {
+            return mController.isMobileDataSupported() ? mController.isMobileDataEnabled() : null;
+        }
+
+        @Override
+        public Intent getSettingsIntent() {
+            return CELLULAR_SETTINGS;
+        }
+
+        @Override
+        public void setToggleState(boolean state) {
+            mController.setMobileDataEnabled(state);
+        }
+
+        @Override
+        public View createDetailView(Context context, View convertView, ViewGroup parent) {
+            final View v = convertView != null ? convertView : LayoutInflater.from(mContext)
+                    .inflate(R.layout.data_usage, parent, false);
+            final DataUsageInfo info = mController.getDataUsageInfo();
+            if (info == null) return v;
+            final TextView title = (TextView) v.findViewById(android.R.id.title);
+            title.setText(R.string.quick_settings_cellular_detail_data_usage);
+            final TextView usage = (TextView) v.findViewById(R.id.usage_text);
+            usage.setText(formatBytes(info.usageLevel));
+            final DataUsageGraph graph = (DataUsageGraph) v.findViewById(R.id.usage_graph);
+            graph.setLevels(info.maxLevel, info.limitLevel, info.warningLevel, info.usageLevel);
+            final TextView carrier = (TextView) v.findViewById(R.id.usage_carrier_text);
+            carrier.setText(info.carrier);
+            final TextView period = (TextView) v.findViewById(R.id.usage_period_text);
+            period.setText(info.period);
+            final TextView infoTop = (TextView) v.findViewById(R.id.usage_info_top_text);
+            // TODO
+            final TextView infoBottom = (TextView) v.findViewById(R.id.usage_info_bottom_text);
+            // TODO
+            return v;
+        }
+
+        public void setMobileDataEnabled(boolean enabled) {
+            fireToggleStateChanged(enabled);
+        }
+
+        private String formatBytes(long bytes) {
+            final long b = Math.abs(bytes);
+            double val;
+            String suffix;
+            if (b > 100 * MB) {
+                val = b / GB;
+                suffix = "GB";
+            } else if (b > 100 * KB) {
+                val = b / MB;
+                suffix = "MB";
+            } else {
+                val = b / KB;
+                suffix = "KB";
+            }
+            return FORMAT.format(val * (bytes < 0 ? -1 : 1)) + " " + suffix;
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
index 5d1b16c..9e86a1e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
@@ -192,6 +192,11 @@
         public void onAirplaneModeChanged(boolean enabled) {
             // noop
         }
+
+        @Override
+        public void onMobileDataEnabled(boolean enabled) {
+            // noop
+        }
     };
 
     private final class WifiDetailAdapter implements DetailAdapter,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
index 186b618..fa8e1a6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -39,20 +39,18 @@
  */
 public class PhoneStatusBarPolicy {
     private static final String TAG = "PhoneStatusBarPolicy";
-
-    // message codes for the handler
-    private static final int EVENT_BATTERY_CLOSE = 4;
-
-    private static final int AM_PM_STYLE_NORMAL  = 0;
-    private static final int AM_PM_STYLE_SMALL   = 1;
-    private static final int AM_PM_STYLE_GONE    = 2;
-
-    private static final int AM_PM_STYLE = AM_PM_STYLE_GONE;
-
-    private static final int INET_CONDITION_THRESHOLD = 50;
+    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
     private static final boolean SHOW_SYNC_ICON = false;
 
+    private static final String SLOT_SYNC_ACTIVE = "sync_active";
+    private static final String SLOT_BLUETOOTH = "bluetooth";
+    private static final String SLOT_TTY = "tty";
+    private static final String SLOT_ZEN = "zen";
+    private static final String SLOT_VOLUME = "volume";
+    private static final String SLOT_CDMA_ERI = "cdma_eri";
+    private static final String SLOT_ALARM_CLOCK = "alarm_clock";
+
     private final Context mContext;
     private final StatusBarManager mService;
     private final Handler mHandler = new Handler();
@@ -61,24 +59,13 @@
     // to get broadcasts that it *is* there.
     IccCardConstants.State mSimState = IccCardConstants.State.READY;
 
-    // ringer volume
+    private boolean mZenVisible;
     private boolean mVolumeVisible;
 
-    // zen mode
     private int mZen;
 
-    // bluetooth device status
     private boolean mBluetoothEnabled = false;
 
-    private int mLastWifiSignalLevel = -1;
-    private boolean mIsWifiConnected = false;
-
-    // state of inet connection - 0 not connected, 100 connected
-    private int mInetCondition = 0;
-
-    // sync state
-    // If sync is active the SyncActive icon is displayed. If sync is not active but
-    // sync is failing the SyncFailing icon is displayed. Otherwise neither are displayed.
 
     private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
         @Override
@@ -95,7 +82,7 @@
                 updateBluetooth(intent);
             }
             else if (action.equals(AudioManager.RINGER_MODE_CHANGED_ACTION)) {
-                updateVolume();
+                updateVolumeZen();
             }
             else if (action.equals(TelephonyIntents.ACTION_SIM_STATE_CHANGED)) {
                 updateSimState(intent);
@@ -122,12 +109,12 @@
         mContext.registerReceiver(mIntentReceiver, filter, null, mHandler);
 
         // TTY status
-        mService.setIcon("tty",  R.drawable.stat_sys_tty_mode, 0, null);
-        mService.setIconVisibility("tty", false);
+        mService.setIcon(SLOT_TTY,  R.drawable.stat_sys_tty_mode, 0, null);
+        mService.setIconVisibility(SLOT_TTY, false);
 
         // Cdma Roaming Indicator, ERI
-        mService.setIcon("cdma_eri", R.drawable.stat_sys_roaming_cdma_0, 0, null);
-        mService.setIconVisibility("cdma_eri", false);
+        mService.setIcon(SLOT_CDMA_ERI, R.drawable.stat_sys_roaming_cdma_0, 0, null);
+        mService.setIconVisibility(SLOT_CDMA_ERI, false);
 
         // bluetooth status
         BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
@@ -138,38 +125,42 @@
                 bluetoothIcon = R.drawable.stat_sys_data_bluetooth_connected;
             }
         }
-        mService.setIcon("bluetooth", bluetoothIcon, 0, null);
-        mService.setIconVisibility("bluetooth", mBluetoothEnabled);
+        mService.setIcon(SLOT_BLUETOOTH, bluetoothIcon, 0, null);
+        mService.setIconVisibility(SLOT_BLUETOOTH, mBluetoothEnabled);
 
         // Alarm clock
-        mService.setIcon("alarm_clock", R.drawable.stat_sys_alarm, 0, null);
-        mService.setIconVisibility("alarm_clock", false);
+        mService.setIcon(SLOT_ALARM_CLOCK, R.drawable.stat_sys_alarm, 0, null);
+        mService.setIconVisibility(SLOT_ALARM_CLOCK, false);
 
         // Sync state
-        mService.setIcon("sync_active", R.drawable.stat_sys_sync, 0, null);
-        mService.setIconVisibility("sync_active", false);
+        mService.setIcon(SLOT_SYNC_ACTIVE, R.drawable.stat_sys_sync, 0, null);
+        mService.setIconVisibility(SLOT_SYNC_ACTIVE, false);
         // "sync_failing" is obsolete: b/1297963
 
+        // zen
+        mService.setIcon(SLOT_ZEN, R.drawable.stat_sys_zen_important, 0, null);
+        mService.setIconVisibility(SLOT_ZEN, false);
+
         // volume
-        mService.setIcon("volume", R.drawable.stat_sys_ringer_silent, 0, null);
-        mService.setIconVisibility("volume", false);
-        updateVolume();
+        mService.setIcon(SLOT_VOLUME, R.drawable.stat_sys_ringer_silent, 0, null);
+        mService.setIconVisibility(SLOT_VOLUME, false);
+        updateVolumeZen();
     }
 
     public void setZenMode(int zen) {
         mZen = zen;
-        updateVolume();
+        updateVolumeZen();
     }
 
     private final void updateAlarm(Intent intent) {
         boolean alarmSet = intent.getBooleanExtra("alarmSet", false);
-        mService.setIconVisibility("alarm_clock", alarmSet);
+        mService.setIconVisibility(SLOT_ALARM_CLOCK, alarmSet);
     }
 
     private final void updateSyncState(Intent intent) {
         if (!SHOW_SYNC_ICON) return;
         boolean isActive = intent.getBooleanExtra("active", false);
-        mService.setIconVisibility("sync_active", isActive);
+        mService.setIconVisibility(SLOT_SYNC_ACTIVE, isActive);
     }
 
     private final void updateSimState(Intent intent) {
@@ -200,32 +191,48 @@
         }
     }
 
-    private final void updateVolume() {
+    private final void updateVolumeZen() {
         AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
-        final int ringerMode = audioManager.getRingerMode();
-        int iconId = 0;
-        String contentDescription = null;
-        boolean visible = false;
-        if (ringerMode == AudioManager.RINGER_MODE_SILENT) {
-            visible = true;
-            iconId = R.drawable.stat_sys_ringer_silent;
-            contentDescription = mContext.getString(R.string.accessibility_ringer_silent);
+
+        boolean zenVisible = false;
+        int zenIconId = 0;
+        String zenDescription = null;
+
+        boolean volumeVisible = false;
+        int volumeIconId = 0;
+        String volumeDescription = null;
+
+        if (mZen == Global.ZEN_MODE_NO_INTERRUPTIONS) {
+            zenVisible = true;
+            zenIconId = R.drawable.stat_sys_zen_none;
+            zenDescription = mContext.getString(R.string.zen_no_interruptions);
         } else if (mZen == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS) {
-            visible = true;
-            iconId = R.drawable.stat_sys_zen_important;
-            contentDescription = mContext.getString(R.string.zen_important_interruptions);
-        } else if (ringerMode == AudioManager.RINGER_MODE_VIBRATE) {
-            visible = true;
-            iconId = R.drawable.stat_sys_ringer_vibrate;
-            contentDescription = mContext.getString(R.string.accessibility_ringer_vibrate);
+            zenVisible = true;
+            zenIconId = R.drawable.stat_sys_zen_important;
+            zenDescription = mContext.getString(R.string.zen_important_interruptions);
         }
 
-        if (visible) {
-            mService.setIcon("volume", iconId, 0, contentDescription);
+        if (mZen != Global.ZEN_MODE_NO_INTERRUPTIONS &&
+                audioManager.getRingerMode() == AudioManager.RINGER_MODE_VIBRATE) {
+            volumeVisible = true;
+            volumeIconId = R.drawable.stat_sys_ringer_vibrate;
+            volumeDescription = mContext.getString(R.string.accessibility_ringer_vibrate);
         }
-        if (visible != mVolumeVisible) {
-            mService.setIconVisibility("volume", visible);
-            mVolumeVisible = visible;
+
+        if (zenVisible) {
+            mService.setIcon(SLOT_ZEN, zenIconId, 0, zenDescription);
+        }
+        if (zenVisible != mZenVisible) {
+            mService.setIconVisibility(SLOT_ZEN, zenVisible);
+            mZenVisible = zenVisible;
+        }
+
+        if (volumeVisible) {
+            mService.setIcon(SLOT_VOLUME, volumeIconId, 0, volumeDescription);
+        }
+        if (volumeVisible != mVolumeVisible) {
+            mService.setIconVisibility(SLOT_VOLUME, volumeVisible);
+            mVolumeVisible = volumeVisible;
         }
     }
 
@@ -250,26 +257,25 @@
             return;
         }
 
-        mService.setIcon("bluetooth", iconId, 0, contentDescription);
-        mService.setIconVisibility("bluetooth", mBluetoothEnabled);
+        mService.setIcon(SLOT_BLUETOOTH, iconId, 0, contentDescription);
+        mService.setIconVisibility(SLOT_BLUETOOTH, mBluetoothEnabled);
     }
 
     private final void updateTTY(Intent intent) {
-        final String action = intent.getAction();
         final boolean enabled = intent.getBooleanExtra(TtyIntent.TTY_ENABLED, false);
 
-        if (false) Log.v(TAG, "updateTTY: enabled: " + enabled);
+        if (DEBUG) Log.v(TAG, "updateTTY: enabled: " + enabled);
 
         if (enabled) {
             // TTY is on
-            if (false) Log.v(TAG, "updateTTY: set TTY on");
-            mService.setIcon("tty", R.drawable.stat_sys_tty_mode, 0,
+            if (DEBUG) Log.v(TAG, "updateTTY: set TTY on");
+            mService.setIcon(SLOT_TTY, R.drawable.stat_sys_tty_mode, 0,
                     mContext.getString(R.string.accessibility_tty_enabled));
-            mService.setIconVisibility("tty", true);
+            mService.setIconVisibility(SLOT_TTY, true);
         } else {
             // TTY is off
-            if (false) Log.v(TAG, "updateTTY: set TTY off");
-            mService.setIconVisibility("tty", false);
+            if (DEBUG) Log.v(TAG, "updateTTY: set TTY off");
+            mService.setIconVisibility(SLOT_TTY, false);
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileDataController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileDataController.java
new file mode 100644
index 0000000..40549e8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileDataController.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.policy;
+
+import static android.net.ConnectivityManager.TYPE_MOBILE;
+import static android.net.NetworkStatsHistory.FIELD_RX_BYTES;
+import static android.net.NetworkStatsHistory.FIELD_TX_BYTES;
+import static android.telephony.TelephonyManager.SIM_STATE_READY;
+
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.INetworkStatsService;
+import android.net.INetworkStatsSession;
+import android.net.NetworkStatsHistory;
+import android.net.NetworkTemplate;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.telephony.TelephonyManager;
+import android.text.format.DateUtils;
+import android.util.Log;
+
+import com.android.systemui.statusbar.policy.NetworkController.DataUsageInfo;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+public class MobileDataController {
+    private static final String TAG = "MobileDataController";
+    private static final boolean DEBUG = true;
+
+    private static final SimpleDateFormat MMM_D = new SimpleDateFormat("MMM d");
+    private static final int FIELDS = FIELD_RX_BYTES | FIELD_TX_BYTES;
+
+    private final Context mContext;
+    private final TelephonyManager mTelephonyManager;
+    private final ConnectivityManager mConnectivityManager;
+    private final INetworkStatsService mStatsService;
+
+    private INetworkStatsSession mSession;
+    private Callback mCallback;
+
+    public MobileDataController(Context context) {
+        mContext = context;
+        mTelephonyManager = TelephonyManager.from(context);
+        mConnectivityManager = ConnectivityManager.from(context);
+        mStatsService = INetworkStatsService.Stub.asInterface(
+                ServiceManager.getService(Context.NETWORK_STATS_SERVICE));
+        try {
+            mSession = mStatsService.openSession();
+        } catch (RemoteException e) {
+            Log.w(TAG, "Failed to open stats session");
+            mSession = null;
+        }
+    }
+
+    public void setCallback(Callback callback) {
+        mCallback = callback;
+    }
+
+    private DataUsageInfo warn(String msg) {
+        Log.w(TAG, "Failed to get data usage, " + msg);
+        return null;
+    }
+
+    public DataUsageInfo getDataUsageInfo() {
+        final String subscriberId = getActiveSubscriberId(mContext);
+        if (subscriberId == null) {
+            return warn("no subscriber id");
+        }
+        if (mSession == null) {
+            return warn("no stats session");
+        }
+        final NetworkTemplate template = NetworkTemplate.buildTemplateMobileAll(subscriberId);
+        try {
+            final NetworkStatsHistory history = mSession.getHistoryForNetwork(template, FIELDS);
+            final long now = System.currentTimeMillis();
+            // period = last 4 wks for now
+            final long start = now - DateUtils.WEEK_IN_MILLIS * 4;
+            final long end = now;
+            final long callStart = System.currentTimeMillis();
+            final NetworkStatsHistory.Entry entry = history.getValues(start, end, now, null);
+            final long callEnd = System.currentTimeMillis();
+            if (DEBUG) Log.d(TAG, String.format("history call from %s to %s now=%s took %sms: %s",
+                    new Date(start), new Date(end), new Date(now), callEnd - callStart,
+                    historyEntryToString(entry)));
+            if (entry == null) {
+                return warn("no entry data");
+            }
+            final long totalBytes = entry.rxBytes + entry.txBytes;
+            final DataUsageInfo usage = new DataUsageInfo();
+            usage.maxLevel = (long) (totalBytes / .4);
+            usage.usageLevel = totalBytes;
+            usage.period = MMM_D.format(new Date(start)) + " - " + MMM_D.format(new Date(end));
+            return usage;
+        } catch (RemoteException e) {
+            return warn("remote call failed");
+        }
+    }
+
+    private static String historyEntryToString(NetworkStatsHistory.Entry entry) {
+        return entry == null ? null : new StringBuilder("Entry[")
+                .append("bucketDuration=").append(entry.bucketDuration)
+                .append(",bucketStart=").append(entry.bucketStart)
+                .append(",activeTime=").append(entry.activeTime)
+                .append(",rxBytes=").append(entry.rxBytes)
+                .append(",rxPackets=").append(entry.rxPackets)
+                .append(",txBytes=").append(entry.txBytes)
+                .append(",txPackets=").append(entry.txPackets)
+                .append(",operations=").append(entry.operations)
+                .append(']').toString();
+    }
+
+    public void setMobileDataEnabled(boolean enabled) {
+        mTelephonyManager.setDataEnabled(enabled);
+        if (mCallback != null) {
+            mCallback.onMobileDataEnabled(enabled);
+        }
+    }
+
+    public boolean isMobileDataSupported() {
+        // require both supported network and ready SIM
+        return mConnectivityManager.isNetworkSupported(TYPE_MOBILE)
+                && mTelephonyManager.getSimState() == SIM_STATE_READY;
+    }
+
+    public boolean isMobileDataEnabled() {
+        return mTelephonyManager.getDataEnabled();
+    }
+
+    private static String getActiveSubscriberId(Context context) {
+        final TelephonyManager tele = TelephonyManager.from(context);
+        final String actualSubscriberId = tele.getSubscriberId();
+        return actualSubscriberId;
+    }
+
+    public interface Callback {
+        void onMobileDataEnabled(boolean enabled);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
index 7e11369..d058bd0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
@@ -32,12 +32,17 @@
                 boolean activityIn, boolean activityOut,
                 String dataTypeContentDescriptionId, String description, boolean noSim);
         void onAirplaneModeChanged(boolean enabled);
+        void onMobileDataEnabled(boolean enabled);
     }
 
     void addAccessPointCallback(AccessPointCallback callback);
     void removeAccessPointCallback(AccessPointCallback callback);
     void scanForAccessPoints();
     void connect(AccessPoint ap);
+    boolean isMobileDataSupported();
+    boolean isMobileDataEnabled();
+    void setMobileDataEnabled(boolean enabled);
+    DataUsageInfo getDataUsageInfo();
 
     public interface AccessPointCallback {
         void onAccessPointsChanged(AccessPoint[] accessPoints);
@@ -52,4 +57,13 @@
         public boolean isConnected;
         public int level;  // 0 - 5
     }
+
+    public static class DataUsageInfo {
+        public String carrier;
+        public String period;
+        public long maxLevel;
+        public long limitLevel;
+        public long warningLevel;
+        public long usageLevel;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
index 4b94ebd..799b41f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -170,6 +170,7 @@
     }
 
     private final WifiAccessPointController mAccessPoints;
+    private final MobileDataController mMobileDataController;
 
     /**
      * Construct this controller object and register for updates.
@@ -240,6 +241,19 @@
 
         mLastLocale = mContext.getResources().getConfiguration().locale;
         mAccessPoints = new WifiAccessPointController(mContext);
+        mMobileDataController = new MobileDataController(mContext);
+        mMobileDataController.setCallback(new MobileDataController.Callback() {
+            @Override
+            public void onMobileDataEnabled(boolean enabled) {
+                notifyMobileDataEnabled(enabled);
+            }
+        });
+    }
+
+    private void notifyMobileDataEnabled(boolean enabled) {
+        for (NetworkSignalChangedCallback cb : mSignalsChangedCallbacks) {
+            cb.onMobileDataEnabled(enabled);
+        }
     }
 
     public boolean hasMobileDataFeature() {
@@ -322,6 +336,28 @@
         }.execute();
     }
 
+    @Override
+    public DataUsageInfo getDataUsageInfo() {
+        final DataUsageInfo info =  mMobileDataController.getDataUsageInfo();
+        info.carrier = mNetworkName;
+        return info;
+    }
+
+    @Override
+    public boolean isMobileDataSupported() {
+        return mMobileDataController.isMobileDataSupported();
+    }
+
+    @Override
+    public boolean isMobileDataEnabled() {
+        return mMobileDataController.isMobileDataEnabled();
+    }
+
+    @Override
+    public void setMobileDataEnabled(boolean enabled) {
+        mMobileDataController.setMobileDataEnabled(enabled);
+    }
+
     public void refreshSignalCluster(SignalCluster cluster) {
         if (mDemoMode) return;
         cluster.setWifiIndicators(
diff --git a/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java b/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
index afa5bfe..b9d07d5 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
@@ -279,8 +279,7 @@
         mZenSubheadCollapsed.setVisibility(!mExpanded ? VISIBLE : GONE);
         mMoreSettings.setVisibility(zenImportant && mExpanded ? VISIBLE : GONE);
         mZenConditions.setVisibility(!zenOff && mExpanded ? VISIBLE : GONE);
-        mAlarmWarning.setVisibility(zenNone && mExpanded && mController != null
-                && mController.hasNextAlarm() ? VISIBLE : GONE);
+        mAlarmWarning.setVisibility(zenNone && mExpanded ? VISIBLE : GONE);
 
         if (zenNone) {
             mZenSubheadExpanded.setText(R.string.zen_no_interruptions);
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index b31a3d6..5bfde4d 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -6537,10 +6537,10 @@
                     }
 
                     // Pull the Package Manager metadata from the restore set first
-                    PackageInfo omPackage = new PackageInfo();
-                    omPackage.packageName = PACKAGE_MANAGER_SENTINEL;
+                    mCurrentPackage = new PackageInfo();
+                    mCurrentPackage.packageName = PACKAGE_MANAGER_SENTINEL;
                     mPmAgent = new PackageManagerBackupAgent(mPackageManager, null);
-                    initiateOneRestore(omPackage, 0,
+                    initiateOneRestore(mCurrentPackage, 0,
                             IBackupAgent.Stub.asInterface(mPmAgent.onBind()));
                     // The PM agent called operationComplete() already, because our invocation
                     // of it is process-local and therefore synchronous.  That means that a
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index e865e88..13ad5d2 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -60,7 +60,6 @@
 import android.database.ContentObserver;
 import android.net.CaptivePortalTracker;
 import android.net.ConnectivityManager;
-import android.net.DummyDataStateTracker;
 import android.net.IConnectivityManager;
 import android.net.INetworkManagementEventObserver;
 import android.net.INetworkPolicyListener;
@@ -925,8 +924,6 @@
         @Override
         public NetworkStateTracker createTracker(int targetNetworkType, NetworkConfig config) {
             switch (config.radio) {
-                case TYPE_DUMMY:
-                    return new DummyDataStateTracker(targetNetworkType, config.name);
                 case TYPE_WIMAX:
                     return makeWimaxStateTracker(mContext, mTrackerHandler);
                 case TYPE_PROXY:
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 1da8123..8257f29 100755
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -5608,7 +5608,22 @@
             }
         }
     }
-    
+
+    @Override
+    public final void mediaResourcesReleased(IBinder token) {
+        final long origId = Binder.clearCallingIdentity();
+        try {
+            synchronized (this) {
+                ActivityStack stack = ActivityRecord.getStackLocked(token);
+                if (stack != null) {
+                    stack.mediaResourcesReleased(token);
+                }
+            }
+        } finally {
+            Binder.restoreCallingIdentity(origId);
+        }
+    }
+
     @Override
     public String getCallingPackage(IBinder token) {
         synchronized (this) {
@@ -7945,7 +7960,7 @@
                 checkedGrants = true;
             }
             userId = handleIncomingUser(callingPid, callingUid, userId,
-                    false, true, "checkContentProviderPermissionLocked " + cpi.authority, null);
+                    false, false, "checkContentProviderPermissionLocked " + cpi.authority, null);
         }
         if (checkComponentPermission(cpi.readPermission, callingPid, callingUid,
                 cpi.applicationInfo.uid, cpi.exported)
@@ -9347,6 +9362,7 @@
                 }
                 if (r.changeWindowTranslucency(true)) {
                     mWindowManager.setAppFullscreen(token, true);
+                    r.task.stack.releaseMediaResources();
                     mStackSupervisor.ensureActivitiesVisibleLocked(null, 0);
                     return true;
                 }
@@ -9383,6 +9399,38 @@
     }
 
     @Override
+    public boolean setMediaPlaying(IBinder token, boolean playing) {
+        final long origId = Binder.clearCallingIdentity();
+        try {
+            synchronized (this) {
+                final ActivityRecord r = ActivityRecord.isInStackLocked(token);
+                if (r != null) {
+                    return mStackSupervisor.setMediaPlayingLocked(r, playing);
+                }
+            }
+            return false;
+        } finally {
+            Binder.restoreCallingIdentity(origId);
+        }
+    }
+
+    @Override
+    public boolean isBackgroundMediaPlaying(IBinder token) {
+        final long origId = Binder.clearCallingIdentity();
+        try {
+            synchronized (this) {
+                final ActivityStack stack = ActivityRecord.getStackLocked(token);
+                final boolean playing = stack == null ? false : stack.isMediaPlaying();
+                if (ActivityStackSupervisor.DEBUG_MEDIA_VISIBILITY) Slog.d(TAG,
+                        "isBackgroundMediaPlaying: stack=" + stack + " playing=" + playing);
+                return playing;
+            }
+        } finally {
+            Binder.restoreCallingIdentity(origId);
+        }
+    }
+
+    @Override
     public ActivityOptions getActivityOptions(IBinder token) {
         final long origId = Binder.clearCallingIdentity();
         try {
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index fd2a0b1..d70e8b7 100755
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -203,6 +203,14 @@
                     pw.print(" resultWho="); pw.print(resultWho);
                     pw.print(" resultCode="); pw.println(requestCode);
         }
+        if (taskDescription.getIcon() != null || taskDescription.getLabel() != null ||
+                taskDescription.getPrimaryColor() != 0) {
+            pw.print(prefix); pw.print("taskDescription:");
+                    pw.print(" icon="); pw.print(taskDescription.getIcon());
+                    pw.print(" label=\""); pw.print(taskDescription.getLabel()); pw.print("\"");
+                    pw.print(" color=");
+                    pw.println(Integer.toHexString(taskDescription.getPrimaryColor()));
+        }
         if (results != null) {
             pw.print(prefix); pw.print("results="); pw.println(results);
         }
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index ca518f3..c6bba22 100755
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -32,7 +32,6 @@
 
 import static com.android.server.am.ActivityRecord.HOME_ACTIVITY_TYPE;
 import static com.android.server.am.ActivityRecord.APPLICATION_ACTIVITY_TYPE;
-import static com.android.server.am.ActivityRecord.RECENTS_ACTIVITY_TYPE;
 
 import static com.android.server.am.ActivityStackSupervisor.DEBUG_ADD_REMOVE;
 import static com.android.server.am.ActivityStackSupervisor.DEBUG_APP;
@@ -47,6 +46,7 @@
 import com.android.server.Watchdog;
 import com.android.server.am.ActivityManagerService.ItemMatcher;
 import com.android.server.am.ActivityStackSupervisor.ActivityContainer;
+import com.android.server.am.ActivityStackSupervisor.ActivityDisplay;
 import com.android.server.wm.AppTransition;
 import com.android.server.wm.TaskGroup;
 import com.android.server.wm.WindowManagerService;
@@ -252,14 +252,14 @@
     static final int STOP_TIMEOUT_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 4;
     static final int DESTROY_ACTIVITIES_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 5;
     static final int TRANSLUCENT_TIMEOUT_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 6;
+    static final int STOP_MEDIA_PLAYING_TIMEOUT_MSG =
+            ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 7;
 
     static class ScheduleDestroyArgs {
         final ProcessRecord mOwner;
-        final boolean mOomAdj;
         final String mReason;
-        ScheduleDestroyArgs(ProcessRecord owner, boolean oomAdj, String reason) {
+        ScheduleDestroyArgs(ProcessRecord owner, String reason) {
             mOwner = owner;
-            mOomAdj = oomAdj;
             mReason = reason;
         }
     }
@@ -320,7 +320,7 @@
                 case DESTROY_ACTIVITIES_MSG: {
                     ScheduleDestroyArgs args = (ScheduleDestroyArgs)msg.obj;
                     synchronized (mService) {
-                        destroyActivitiesLocked(args.mOwner, args.mOomAdj, args.mReason);
+                        destroyActivitiesLocked(args.mOwner, args.mReason);
                     }
                 } break;
                 case TRANSLUCENT_TIMEOUT_MSG: {
@@ -328,6 +328,15 @@
                         notifyActivityDrawnLocked(null);
                     }
                 } break;
+                case STOP_MEDIA_PLAYING_TIMEOUT_MSG: {
+                    synchronized (mService) {
+                        final ActivityRecord r = getMediaPlayer();
+                        Slog.e(TAG, "Timeout waiting for stopMediaPlaying player=" + r);
+                        if (r != null) {
+                            mService.killAppAtUsersRequest(r.app, null);
+                        }
+                    }
+                } break;
             }
         }
     }
@@ -930,11 +939,14 @@
             mHandler.removeMessages(STOP_TIMEOUT_MSG, r);
             r.stopped = true;
             r.state = ActivityState.STOPPED;
+            if (mActivityContainer.mActivityDisplay.mMediaPlayingActivity == r) {
+                mStackSupervisor.setMediaPlayingLocked(r, false);
+            }
             if (r.finishing) {
                 r.clearOptionsLocked();
             } else {
                 if (r.configDestroy) {
-                    destroyActivityLocked(r, true, false, "stop-config");
+                    destroyActivityLocked(r, true, "stop-config");
                     mStackSupervisor.resumeTopActivitiesLocked();
                 } else {
                     mStackSupervisor.updatePreviousProcessLocked(r);
@@ -966,8 +978,10 @@
                     // instance right now, we need to first completely stop
                     // the current instance before starting the new one.
                     if (DEBUG_PAUSE) Slog.v(TAG, "Destroying after pause: " + prev);
-                    destroyActivityLocked(prev, true, false, "pause-config");
-                } else {
+                    destroyActivityLocked(prev, true, "pause-config");
+                } else if (!isMediaPlaying()) {
+                    // If we were playing then resumeTopActivities will release resources before
+                    // stopping.
                     mStackSupervisor.mStoppingActivities.add(prev);
                     if (mStackSupervisor.mStoppingActivities.size() > 3 ||
                             prev.frontOfTask && mTaskHistory.size() <= 1) {
@@ -1280,10 +1294,14 @@
                                 case PAUSED:
                                     // This case created for transitioning activities from
                                     // translucent to opaque {@link Activity#convertToOpaque}.
-                                    if (!mStackSupervisor.mStoppingActivities.contains(r)) {
-                                        mStackSupervisor.mStoppingActivities.add(r);
+                                    if (getMediaPlayer() == r) {
+                                        releaseMediaResources();
+                                    } else {
+                                        if (!mStackSupervisor.mStoppingActivities.contains(r)) {
+                                            mStackSupervisor.mStoppingActivities.add(r);
+                                        }
+                                        mStackSupervisor.scheduleIdleLocked();
                                     }
-                                    mStackSupervisor.scheduleIdleLocked();
                                     break;
 
                                 default:
@@ -1548,8 +1566,6 @@
             // very soon and it would be a waste to let it get killed if it
             // happens to be sitting towards the end.
             if (next.app != null && next.app.thread != null) {
-                // No reason to do full oom adj update here; we'll let that
-                // happen whenever it needs to later.
                 mService.updateLruProcessLocked(next.app, true, null);
             }
             if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
@@ -2433,7 +2449,7 @@
                 if (DEBUG_STATES) Slog.v(TAG, "Stop failed; moving to STOPPED: " + r);
                 r.state = ActivityState.STOPPED;
                 if (r.configDestroy) {
-                    destroyActivityLocked(r, true, false, "stop-except");
+                    destroyActivityLocked(r, true, "stop-except");
                 }
             }
         }
@@ -2693,7 +2709,7 @@
             // If this activity is already stopped, we can just finish
             // it right now.
             r.makeFinishing();
-            boolean activityRemoved = destroyActivityLocked(r, true, oomAdj, "finish-imm");
+            boolean activityRemoved = destroyActivityLocked(r, true, "finish-imm");
             if (activityRemoved) {
                 mStackSupervisor.resumeTopActivitiesLocked();
             }
@@ -2856,6 +2872,9 @@
 
         // Get rid of any pending idle timeouts.
         removeTimeoutsForActivityLocked(r);
+        if (getMediaPlayer() == r) {
+            mStackSupervisor.setMediaPlayingLocked(r, false);
+        }
     }
 
     private void removeTimeoutsForActivityLocked(ActivityRecord r) {
@@ -2914,13 +2933,13 @@
         }
     }
 
-    final void scheduleDestroyActivities(ProcessRecord owner, boolean oomAdj, String reason) {
+    final void scheduleDestroyActivities(ProcessRecord owner, String reason) {
         Message msg = mHandler.obtainMessage(DESTROY_ACTIVITIES_MSG);
-        msg.obj = new ScheduleDestroyArgs(owner, oomAdj, reason);
+        msg.obj = new ScheduleDestroyArgs(owner, reason);
         mHandler.sendMessage(msg);
     }
 
-    final void destroyActivitiesLocked(ProcessRecord owner, boolean oomAdj, String reason) {
+    final void destroyActivitiesLocked(ProcessRecord owner, String reason) {
         boolean lastIsOpaque = false;
         boolean activityRemoved = false;
         for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
@@ -2948,7 +2967,7 @@
                     if (DEBUG_SWITCH) Slog.v(TAG, "Destroying " + r + " in state " + r.state
                             + " resumed=" + mResumedActivity
                             + " pausing=" + mPausingActivity);
-                    if (destroyActivityLocked(r, true, oomAdj, reason)) {
+                    if (destroyActivityLocked(r, true, reason)) {
                         activityRemoved = true;
                     }
                 }
@@ -2965,8 +2984,7 @@
      * a configuration switch where we destroy the current client-side object
      * but then create a new client-side object for this same HistoryRecord.
      */
-    final boolean destroyActivityLocked(ActivityRecord r,
-            boolean removeFromApp, boolean oomAdj, String reason) {
+    final boolean destroyActivityLocked(ActivityRecord r, boolean removeFromApp, String reason) {
         if (DEBUG_SWITCH || DEBUG_CLEANUP) Slog.v(
             TAG, "Removing activity from " + reason + ": token=" + r
               + ", app=" + (r.app != null ? r.app.processName : "(null)"));
@@ -3077,6 +3095,49 @@
         }
     }
 
+    void releaseMediaResources() {
+        if (isMediaPlaying() && !mHandler.hasMessages(STOP_MEDIA_PLAYING_TIMEOUT_MSG)) {
+            final ActivityRecord r = getMediaPlayer();
+            if (DEBUG_STATES) Slog.d(TAG, "releaseMediaResources activtyDisplay=" +
+                    mActivityContainer.mActivityDisplay + " mediaPlayer=" + r + " app=" + r.app +
+                    " thread=" + r.app.thread);
+            if (r != null && r.app != null && r.app.thread != null) {
+                try {
+                    r.app.thread.scheduleStopMediaPlaying(r.appToken);
+                } catch (RemoteException e) {
+                }
+                mHandler.sendEmptyMessageDelayed(STOP_MEDIA_PLAYING_TIMEOUT_MSG, 500);
+            } else {
+                Slog.e(TAG, "releaseMediaResources: activity " + r + " no longer running");
+                mediaResourcesReleased(r.appToken);
+            }
+        }
+    }
+
+    final void mediaResourcesReleased(IBinder token) {
+        mHandler.removeMessages(STOP_MEDIA_PLAYING_TIMEOUT_MSG);
+        final ActivityRecord r = getMediaPlayer();
+        if (r != null) {
+            mStackSupervisor.mStoppingActivities.add(r);
+            setMediaPlayer(null);
+        }
+        mStackSupervisor.resumeTopActivitiesLocked();
+    }
+
+    boolean isMediaPlaying() {
+        return isAttached() && mActivityContainer.mActivityDisplay.isMediaPlaying();
+    }
+
+    void setMediaPlayer(ActivityRecord r) {
+        if (isAttached()) {
+            mActivityContainer.mActivityDisplay.setMediaPlaying(r);
+        }
+    }
+
+    ActivityRecord getMediaPlayer() {
+        return isAttached() ? mActivityContainer.mActivityDisplay.mMediaPlayingActivity : null;
+    }
+
     private void removeHistoryRecordsForAppLocked(ArrayList<ActivityRecord> list,
             ProcessRecord app, String listName) {
         int i = list.size();
@@ -3448,7 +3509,7 @@
             if (r.app == null || r.app.thread == null) {
                 if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,
                         "Config is destroying non-running " + r);
-                destroyActivityLocked(r, true, false, "config");
+                destroyActivityLocked(r, true, "config");
             } else if (r.state == ActivityState.PAUSING) {
                 // A little annoying: we are waiting for this activity to
                 // finish pausing.  Let's not do anything now, but just
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index c4423de..65afd71 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -49,7 +49,6 @@
 import android.app.IActivityManager.WaitResult;
 import android.app.ResultInfo;
 import android.app.StatusBarManager;
-import android.app.admin.DevicePolicyManager;
 import android.app.admin.IDevicePolicyManager;
 import android.content.ComponentName;
 import android.content.Context;
@@ -116,6 +115,7 @@
     static final boolean DEBUG_APP = DEBUG || false;
     static final boolean DEBUG_CONTAINERS = DEBUG || false;
     static final boolean DEBUG_IDLE = DEBUG || false;
+    static final boolean DEBUG_MEDIA_VISIBILITY = DEBUG || false;
     static final boolean DEBUG_SAVED_STATE = DEBUG || false;
     static final boolean DEBUG_SCREENSHOTS = DEBUG || false;
     static final boolean DEBUG_STATES = DEBUG || false;
@@ -2102,7 +2102,7 @@
         // waiting for the next one to start.
         for (int i = 0; i < NF; i++) {
             r = finishes.get(i);
-            activityRemoved |= r.task.stack.destroyActivityLocked(r, true, false, "finish-idle");
+            activityRemoved |= r.task.stack.destroyActivityLocked(r, true, "finish-idle");
         }
 
         if (booting) {
@@ -2298,6 +2298,14 @@
     }
 
     IBinder getHomeActivityToken() {
+        ActivityRecord homeActivity = getHomeActivity();
+        if (homeActivity != null) {
+            return homeActivity.appToken;
+        }
+        return null;
+    }
+
+    ActivityRecord getHomeActivity() {
         final ArrayList<TaskRecord> tasks = mHomeStack.getAllTasks();
         for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
             final TaskRecord task = tasks.get(taskNdx);
@@ -2306,7 +2314,7 @@
                 for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
                     final ActivityRecord r = activities.get(activityNdx);
                     if (r.isHomeActivity()) {
-                        return r.appToken;
+                        return r;
                     }
                 }
             }
@@ -2594,6 +2602,42 @@
         }
     }
 
+    boolean setMediaPlayingLocked(ActivityRecord r, boolean playing) {
+        final ActivityStack stack = r.task.stack;
+        if (stack == null) {
+            if (DEBUG_MEDIA_VISIBILITY) Slog.d(TAG, "setMediaPlaying: r=" + r + " playing=" +
+                    playing + " stack is null");
+            return false;
+        }
+        final boolean isPlaying = stack.isMediaPlaying();
+        if (DEBUG_MEDIA_VISIBILITY) Slog.d(TAG, "setMediaPlayer: r=" + r + " playing=" +
+                playing + " isPlaying=" + isPlaying);
+
+        final ActivityRecord top = topRunningActivityLocked();
+        if (top == null || top == r || (playing == isPlaying)) {
+            if (DEBUG_MEDIA_VISIBILITY) Slog.d(TAG, "setMediaPlaying: quick return");
+            stack.setMediaPlayer(playing ? r : null);
+            return true;
+        }
+
+        // A non-top activity is reporting a visibility change.
+        if (top.fullscreen || top.state != ActivityState.RESUMED || top.app == null ||
+                top.app.thread == null) {
+            // Can't carry out this request.
+            if (DEBUG_MEDIA_VISIBILITY) Slog.d(TAG, "setMediaPlaying: returning top.fullscreen=" +
+                    top.fullscreen+ " top.state=" + top.state + " top.app=" + top.app +
+                    " top.app.thread=" + top.app.thread);
+            return false;
+        }
+
+        stack.setMediaPlayer(playing ? r : null);
+        try {
+            top.app.thread.scheduleBackgroundMediaPlayingChanged(top.appToken, playing);
+        } catch (RemoteException e) {
+        }
+        return true;
+    }
+
     void ensureActivitiesVisibleLocked(ActivityRecord starting, int configChanges) {
         // First the front stacks. In case any are not fullscreen and are in front of home.
         for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
@@ -2612,7 +2656,7 @@
             final int numStacks = stacks.size();
             for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) {
                 final ActivityStack stack = stacks.get(stackNdx);
-                stack.scheduleDestroyActivities(app, false, reason);
+                stack.scheduleDestroyActivities(app, reason);
             }
         }
     }
@@ -2774,7 +2818,8 @@
         boolean needSep = false;
         for (int displayNdx = 0; displayNdx < mActivityDisplays.size(); ++displayNdx) {
             ActivityDisplay activityDisplay = mActivityDisplays.valueAt(displayNdx);
-            pw.print("Display #"); pw.println(activityDisplay.mDisplayId);
+            pw.print("Display #"); pw.print(activityDisplay.mDisplayId);
+                    pw.println(" (activities from bottom to top):");
             ArrayList<ActivityStack> stacks = activityDisplay.mStacks;
             final int numStacks = stacks.size();
             for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) {
@@ -3584,6 +3629,8 @@
          * stacks, bottommost behind. Accessed directly by ActivityManager package classes */
         final ArrayList<ActivityStack> mStacks = new ArrayList<ActivityStack>();
 
+        ActivityRecord mMediaPlayingActivity;
+
         ActivityDisplay() {
         }
 
@@ -3615,6 +3662,14 @@
             bounds.y = mDisplayInfo.appHeight;
         }
 
+        void setMediaPlaying(ActivityRecord r) {
+            mMediaPlayingActivity = r;
+        }
+
+        boolean isMediaPlaying() {
+            return mMediaPlayingActivity != null;
+        }
+
         @Override
         public String toString() {
             return "ActivityDisplay={" + mDisplayId + " numStacks=" + mStacks.size() + "}";
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index ad7c0aa..df12995 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -114,8 +114,7 @@
 
     public Vpn(Context context, VpnCallback callback, INetworkManagementService netService,
             IConnectivityManager connService, int userId) {
-        // TODO: create dedicated TYPE_VPN network type
-        super(ConnectivityManager.TYPE_DUMMY);
+        super(ConnectivityManager.TYPE_VPN);
         mContext = context;
         mCallback = callback;
         mConnService = connService;
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index 6b60ea4..0e6265c 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -25,6 +25,7 @@
 import java.io.PrintWriter;
 import java.lang.reflect.Array;
 import java.util.Arrays;
+import java.util.Objects;
 
 /**
  * Holds data about notifications that should not be shared with the
@@ -201,6 +202,10 @@
         return mIntercept;
     }
 
+    public boolean isCategory(String category) {
+        return Objects.equals(category, getNotification().category);
+    }
+
     /**
      * Returns the timestamp to use for time-based sorting in the ranker.
      */
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 92bec14..f74e371 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -21,6 +21,7 @@
 import android.app.Notification;
 import android.app.PendingIntent;
 import android.content.BroadcastReceiver;
+import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
@@ -34,6 +35,7 @@
 import android.os.IBinder;
 import android.provider.Settings.Global;
 import android.service.notification.ZenModeConfig;
+import android.telecomm.TelecommManager;
 import android.util.Slog;
 
 import com.android.internal.R;
@@ -72,17 +74,13 @@
     private final ZenModeConfig mDefaultConfig;
     private final ArrayList<Callback> mCallbacks = new ArrayList<Callback>();
 
+    private ComponentName mDefaultPhoneApp;
     private int mZenMode;
     private ZenModeConfig mConfig;
     private AudioManager mAudioManager;
     private int mPreviousRingerMode = -1;
 
     // temporary, until we update apps to provide metadata
-    private static final Set<String> CALL_PACKAGES = new HashSet<String>(Arrays.asList(
-            "com.google.android.dialer",
-            "com.android.phone",
-            "com.android.example.notificationshowcase"
-            ));
     private static final Set<String> MESSAGE_PACKAGES = new HashSet<String>(Arrays.asList(
             "com.google.android.talk",
             "com.android.mms",
@@ -224,7 +222,7 @@
 
     public boolean allowDisable(int what, IBinder token, String pkg) {
         // TODO(cwren): delete this API before the next release. Bug:15344099
-        if (CALL_PACKAGES.contains(pkg)) {
+        if (isDefaultPhoneApp(pkg)) {
             return mZenMode == Global.ZEN_MODE_OFF || mConfig.allowCalls;
         }
         return true;
@@ -236,6 +234,7 @@
         pw.print(prefix); pw.print("mConfig="); pw.println(mConfig);
         pw.print(prefix); pw.print("mDefaultConfig="); pw.println(mDefaultConfig);
         pw.print(prefix); pw.print("mPreviousRingerMode="); pw.println(mPreviousRingerMode);
+        pw.print(prefix); pw.print("mDefaultPhoneApp="); pw.println(mDefaultPhoneApp);
     }
 
     public void readXml(XmlPullParser parser) throws XmlPullParserException, IOException {
@@ -280,7 +279,7 @@
 
     private boolean isSystem(NotificationRecord record) {
         return SYSTEM_PACKAGES.contains(record.sbn.getPackageName())
-                && Notification.CATEGORY_SYSTEM.equals(record.getNotification().category);
+                && record.isCategory(Notification.CATEGORY_SYSTEM);
     }
 
     private boolean isAlarm(NotificationRecord record) {
@@ -288,7 +287,19 @@
     }
 
     private boolean isCall(NotificationRecord record) {
-        return CALL_PACKAGES.contains(record.sbn.getPackageName());
+        return isDefaultPhoneApp(record.sbn.getPackageName())
+                || record.isCategory(Notification.CATEGORY_CALL);
+    }
+
+    private boolean isDefaultPhoneApp(String pkg) {
+        if (mDefaultPhoneApp == null) {
+            final TelecommManager telecomm =
+                    (TelecommManager) mContext.getSystemService(Context.TELECOMM_SERVICE);
+            mDefaultPhoneApp = telecomm != null ? telecomm.getDefaultPhoneApp() : null;
+            Slog.d(TAG, "Default phone app: " + mDefaultPhoneApp);
+        }
+        return pkg != null && mDefaultPhoneApp != null
+                && pkg.equals(mDefaultPhoneApp.getPackageName());
     }
 
     private boolean isMessage(NotificationRecord record) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index b65cf72..911c034a 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -13047,7 +13047,7 @@
     /** Called by UserManagerService */
     void cleanUpUserLILPw(int userHandle) {
         mDirtyUsers.remove(userHandle);
-        mSettings.removeUserLPr(userHandle);
+        mSettings.removeUserLPw(userHandle);
         mPendingBroadcasts.remove(userHandle);
         if (mInstaller != null) {
             // Technically, we shouldn't be doing this with the package lock
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index ff075e3..81ea72c 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -3112,7 +3112,7 @@
         writePackageRestrictionsLPr(userHandle);
     }
 
-    void removeUserLPr(int userId) {
+    void removeUserLPw(int userId) {
         Set<Entry<String, PackageSetting>> entries = mPackages.entrySet();
         for (Entry<String, PackageSetting> entry : entries) {
             entry.getValue().removeUser(userId);
@@ -3123,6 +3123,7 @@
         file = getUserPackagesStateBackupFile(userId);
         file.delete();
         removeCrossProfileIntentFiltersToUserLPr(userId);
+        removeCrossProfilePackagesLPw(userId);
     }
 
     void removeCrossProfileIntentFiltersToUserLPr(int targetUserId) {
@@ -3144,6 +3145,27 @@
         }
     }
 
+    public void removeCrossProfilePackagesLPw(int userId) {
+        synchronized(mCrossProfilePackageInfo) {
+            // userId is the source user
+            if (mCrossProfilePackageInfo.get(userId) != null) {
+                mCrossProfilePackageInfo.remove(userId);
+                writePackageRestrictionsLPr(userId);
+            }
+            // userId is the target user
+            int count = mCrossProfilePackageInfo.size();
+            for (int i = 0; i < count; i++) {
+                int sourceUserId = mCrossProfilePackageInfo.keyAt(i);
+                SparseArray<ArrayList<String>> sourceForwardingInfo =
+                        mCrossProfilePackageInfo.valueAt(i);
+                if (sourceForwardingInfo.get(userId) != null) {
+                    sourceForwardingInfo.remove(userId);
+                    writePackageRestrictionsLPr(sourceUserId);
+                }
+            }
+        }
+    }
+
     // This should be called (at least) whenever an application is removed
     private void setFirstAvailableUid(int uid) {
         if (uid > mFirstAvailableUid) {
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 194e85d..64288a5 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -394,16 +394,21 @@
     @Override
     public void setUserIcon(int userId, Bitmap bitmap) {
         checkManageUsersPermission("update users");
-        synchronized (mPackagesLock) {
-            UserInfo info = mUsers.get(userId);
-            if (info == null || info.partial) {
-                Slog.w(LOG_TAG, "setUserIcon: unknown user #" + userId);
-                return;
+        long ident = Binder.clearCallingIdentity();
+        try {
+            synchronized (mPackagesLock) {
+                UserInfo info = mUsers.get(userId);
+                if (info == null || info.partial) {
+                    Slog.w(LOG_TAG, "setUserIcon: unknown user #" + userId);
+                    return;
+                }
+                writeBitmapLocked(info, bitmap);
+                writeUserLocked(info);
             }
-            writeBitmapLocked(info, bitmap);
-            writeUserLocked(info);
+            sendUserInfoChangedBroadcast(userId);
+        } finally {
+            Binder.restoreCallingIdentity(ident);
         }
-        sendUserInfoChangedBroadcast(userId);
     }
 
     private void sendUserInfoChangedBroadcast(int userId) {
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index 02dd1bf..bf767e2 100644
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -485,6 +485,27 @@
             }
 
             @Override
+            public void onChannelRetuned(Uri channelUri) {
+                synchronized (mLock) {
+                    if (DEBUG) {
+                        Slog.d(TAG, "onChannelRetuned(" + channelUri + ")");
+                    }
+                    if (sessionState.mSession == null || sessionState.mClient == null) {
+                        return;
+                    }
+                    try {
+                        // TODO: Consider adding this channel change in the watch log. When we do
+                        // that, how we can protect the watch log from malicious tv inputs should
+                        // be addressed. e.g. add a field which represents where the channel change
+                        // originated from.
+                        sessionState.mClient.onChannelRetuned(channelUri, sessionState.mSeq);
+                    } catch (RemoteException e) {
+                        Slog.e(TAG, "error in onChannelRetuned");
+                    }
+                }
+            }
+
+            @Override
             public void onSessionEvent(String eventType, Bundle eventArgs) {
                 synchronized (mLock) {
                     if (DEBUG) {
diff --git a/services/core/jni/com_android_server_tv_TvInputHal.cpp b/services/core/jni/com_android_server_tv_TvInputHal.cpp
index 64d418a..a9d5c72 100644
--- a/services/core/jni/com_android_server_tv_TvInputHal.cpp
+++ b/services/core/jni/com_android_server_tv_TvInputHal.cpp
@@ -68,6 +68,166 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 
+class BufferProducerThread : public Thread {
+public:
+    BufferProducerThread(tv_input_device_t* device, int deviceId, const tv_stream_t* stream);
+
+    virtual status_t readyToRun();
+
+    void setSurface(const sp<Surface>& surface);
+    void onCaptured(uint32_t seq, bool succeeded);
+    void shutdown();
+
+private:
+    Mutex mLock;
+    Condition mCondition;
+    sp<Surface> mSurface;
+    tv_input_device_t* mDevice;
+    int mDeviceId;
+    tv_stream_t mStream;
+    sp<ANativeWindowBuffer_t> mBuffer;
+    enum {
+        CAPTURING,
+        CAPTURED,
+        RELEASED,
+    } mBufferState;
+    uint32_t mSeq;
+    bool mShutdown;
+
+    virtual bool threadLoop();
+
+    void setSurfaceLocked(const sp<Surface>& surface);
+};
+
+BufferProducerThread::BufferProducerThread(
+        tv_input_device_t* device, int deviceId, const tv_stream_t* stream)
+    : Thread(false),
+      mDevice(device),
+      mDeviceId(deviceId),
+      mBuffer(NULL),
+      mBufferState(RELEASED),
+      mSeq(0u),
+      mShutdown(false) {
+    memcpy(&mStream, stream, sizeof(mStream));
+}
+
+status_t BufferProducerThread::readyToRun() {
+    sp<ANativeWindow> anw(mSurface);
+    status_t err = native_window_set_usage(anw.get(), mStream.buffer_producer.usage);
+    if (err != NO_ERROR) {
+        return err;
+    }
+    err = native_window_set_buffers_dimensions(
+            anw.get(), mStream.buffer_producer.width, mStream.buffer_producer.height);
+    if (err != NO_ERROR) {
+        return err;
+    }
+    err = native_window_set_buffers_format(anw.get(), mStream.buffer_producer.format);
+    if (err != NO_ERROR) {
+        return err;
+    }
+    return NO_ERROR;
+}
+
+void BufferProducerThread::setSurface(const sp<Surface>& surface) {
+    Mutex::Autolock autoLock(&mLock);
+    setSurfaceLocked(surface);
+}
+
+void BufferProducerThread::setSurfaceLocked(const sp<Surface>& surface) {
+    if (surface == mSurface) {
+        return;
+    }
+
+    if (mBufferState == CAPTURING) {
+        mDevice->cancel_capture(mDevice, mDeviceId, mStream.stream_id, mSeq);
+    }
+    while (mBufferState == CAPTURING) {
+        status_t err = mCondition.waitRelative(mLock, s2ns(1));
+        if (err != NO_ERROR) {
+            ALOGE("error %d while wating for buffer state to change.", err);
+            break;
+        }
+    }
+    mBuffer.clear();
+    mBufferState = RELEASED;
+
+    mSurface = surface;
+    mCondition.broadcast();
+}
+
+void BufferProducerThread::onCaptured(uint32_t seq, bool succeeded) {
+    Mutex::Autolock autoLock(&mLock);
+    if (seq != mSeq) {
+        ALOGW("Incorrect sequence value: expected %u actual %u", mSeq, seq);
+    }
+    if (mBufferState != CAPTURING) {
+        ALOGW("mBufferState != CAPTURING : instead %d", mBufferState);
+    }
+    if (succeeded) {
+        mBufferState = CAPTURED;
+    } else {
+        mBuffer.clear();
+        mBufferState = RELEASED;
+    }
+    mCondition.broadcast();
+}
+
+void BufferProducerThread::shutdown() {
+    Mutex::Autolock autoLock(&mLock);
+    mShutdown = true;
+    setSurfaceLocked(NULL);
+    requestExitAndWait();
+}
+
+bool BufferProducerThread::threadLoop() {
+    Mutex::Autolock autoLock(&mLock);
+
+    status_t err = NO_ERROR;
+    if (mSurface == NULL) {
+        err = mCondition.waitRelative(mLock, s2ns(1));
+        // It's OK to time out here.
+        if (err != NO_ERROR && err != TIMED_OUT) {
+            ALOGE("error %d while wating for non-null surface to be set", err);
+            return false;
+        }
+        return true;
+    }
+    sp<ANativeWindow> anw(mSurface);
+    while (mBufferState == CAPTURING) {
+        err = mCondition.waitRelative(mLock, s2ns(1));
+        if (err != NO_ERROR) {
+            ALOGE("error %d while wating for buffer state to change.", err);
+            return false;
+        }
+    }
+    if (mBufferState == CAPTURED && anw != NULL) {
+        err = anw->queueBuffer(anw.get(), mBuffer.get(), -1);
+        if (err != NO_ERROR) {
+            ALOGE("error %d while queueing buffer to surface", err);
+            return false;
+        }
+        mBuffer.clear();
+        mBufferState = RELEASED;
+    }
+    if (mBuffer == NULL && !mShutdown && anw != NULL) {
+        ANativeWindowBuffer_t* buffer = NULL;
+        err = native_window_dequeue_buffer_and_wait(anw.get(), &buffer);
+        if (err != NO_ERROR) {
+            ALOGE("error %d while dequeueing buffer to surface", err);
+            return false;
+        }
+        mBuffer = buffer;
+        mBufferState = CAPTURING;
+        mDevice->request_capture(mDevice, mDeviceId, mStream.stream_id,
+                                 buffer->handle, ++mSeq);
+    }
+
+    return true;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
 class JTvInputHal {
 public:
     ~JTvInputHal();
@@ -79,23 +239,31 @@
     const tv_stream_config_t* getStreamConfigs(int deviceId, int* numConfigs);
 
 private:
+    // Connection between a surface and a stream.
     class Connection {
     public:
         Connection() {}
 
         sp<Surface> mSurface;
+        tv_stream_type_t mStreamType;
+
+        // Only valid when mStreamType == TV_STREAM_TYPE_INDEPENDENT_VIDEO_SOURCE
         sp<NativeHandle> mSourceHandle;
+        // Only valid when mStreamType == TV_STREAM_TYPE_BUFFER_PRODUCER
+        sp<BufferProducerThread> mThread;
     };
 
     JTvInputHal(JNIEnv* env, jobject thiz, tv_input_device_t* dev);
 
     static void notify(
-            tv_input_device_t* dev,tv_input_event_t* event, void* data);
+            tv_input_device_t* dev, tv_input_event_t* event, void* data);
 
     void onDeviceAvailable(const tv_input_device_info_t& info);
     void onDeviceUnavailable(int deviceId);
     void onStreamConfigurationsChanged(int deviceId);
+    void onCaptured(int deviceId, int streamId, uint32_t seq, bool succeeded);
 
+    Mutex mLock;
     jweak mThiz;
     tv_input_device_t* mDevice;
     tv_input_callback_ops_t mCallback;
@@ -153,11 +321,16 @@
         // Nothing to do
         return NO_ERROR;
     }
-    if (Surface::isValid(connection.mSurface)) {
+    // Clear the surface in the connection.
+    if (connection.mSurface != NULL) {
+        if (connection.mStreamType == TV_STREAM_TYPE_INDEPENDENT_VIDEO_SOURCE) {
+            if (Surface::isValid(connection.mSurface)) {
+                connection.mSurface->setSidebandStream(NULL);
+            }
+        }
         connection.mSurface.clear();
     }
-    connection.mSurface = surface;
-    if (connection.mSourceHandle == NULL) {
+    if (connection.mSourceHandle == NULL && connection.mThread == NULL) {
         // Need to configure stream
         int numConfigs = 0;
         const tv_stream_config_t* configs = NULL;
@@ -177,22 +350,32 @@
             ALOGE("Cannot find a config with given stream ID: %d", streamId);
             return BAD_VALUE;
         }
-        // TODO: handle buffer producer profile.
-        if (configs[configIndex].type !=
-                TV_STREAM_TYPE_INDEPENDENT_VIDEO_SOURCE) {
-            ALOGE("Profiles other than independent video source is not yet "
-                  "supported : type = %d", configs[configIndex].type);
-            return INVALID_OPERATION;
-        }
         tv_stream_t stream;
         stream.stream_id = configs[configIndex].stream_id;
+        if (connection.mStreamType == TV_STREAM_TYPE_BUFFER_PRODUCER) {
+            stream.buffer_producer.width = configs[configIndex].max_video_width;
+            stream.buffer_producer.height = configs[configIndex].max_video_height;
+        }
         if (mDevice->open_stream(mDevice, deviceId, &stream) != 0) {
             ALOGE("Couldn't add stream");
             return UNKNOWN_ERROR;
         }
-        connection.mSourceHandle = NativeHandle::create(
-                stream.sideband_stream_source_handle, false);
+        if (connection.mStreamType == TV_STREAM_TYPE_INDEPENDENT_VIDEO_SOURCE) {
+            connection.mSourceHandle = NativeHandle::create(
+                    stream.sideband_stream_source_handle, false);
+        } else if (connection.mStreamType == TV_STREAM_TYPE_BUFFER_PRODUCER) {
+            if (connection.mThread != NULL) {
+                connection.mThread->shutdown();
+            }
+            connection.mThread = new BufferProducerThread(mDevice, deviceId, &stream);
+            connection.mThread->run();
+        }
+    }
+    connection.mSurface = surface;
+    if (connection.mStreamType == TV_STREAM_TYPE_INDEPENDENT_VIDEO_SOURCE) {
         connection.mSurface->setSidebandStream(connection.mSourceHandle);
+    } else if (connection.mStreamType == TV_STREAM_TYPE_BUFFER_PRODUCER) {
+        connection.mThread->setSurface(surface);
     }
     return NO_ERROR;
 }
@@ -220,6 +403,12 @@
             ALOGE("Couldn't remove stream");
             return BAD_VALUE;
         }
+
+        // Clear everything
+        if (connection.mThread != NULL) {
+            connection.mThread->shutdown();
+            connection.mThread.clear();
+        }
         connection.mSourceHandle.clear();
     }
     return NO_ERROR;
@@ -249,14 +438,29 @@
         case TV_INPUT_EVENT_STREAM_CONFIGURATIONS_CHANGED: {
             thiz->onStreamConfigurationsChanged(event->device_info.device_id);
         } break;
+        case TV_INPUT_EVENT_CAPTURE_SUCCEEDED: {
+            thiz->onCaptured(event->capture_result.device_id,
+                             event->capture_result.stream_id,
+                             event->capture_result.seq,
+                             true /* succeeded */);
+        } break;
+        case TV_INPUT_EVENT_CAPTURE_FAILED: {
+            thiz->onCaptured(event->capture_result.device_id,
+                             event->capture_result.stream_id,
+                             event->capture_result.seq,
+                             false /* succeeded */);
+        } break;
         default:
             ALOGE("Unrecognizable event");
     }
 }
 
 void JTvInputHal::onDeviceAvailable(const tv_input_device_info_t& info) {
+    {
+        Mutex::Autolock autoLock(&mLock);
+        mConnections.add(info.device_id, KeyedVector<int, Connection>());
+    }
     JNIEnv* env = AndroidRuntime::getJNIEnv();
-    mConnections.add(info.device_id, KeyedVector<int, Connection>());
 
     jobject builder = env->NewObject(
             gTvInputHardwareInfoBuilderClassInfo.clazz,
@@ -290,13 +494,16 @@
 }
 
 void JTvInputHal::onDeviceUnavailable(int deviceId) {
-    JNIEnv* env = AndroidRuntime::getJNIEnv();
-    KeyedVector<int, Connection>& connections = mConnections.editValueFor(deviceId);
-    for (size_t i = 0; i < connections.size(); ++i) {
-        removeStream(deviceId, connections.keyAt(i));
+    {
+        Mutex::Autolock autoLock(&mLock);
+        KeyedVector<int, Connection>& connections = mConnections.editValueFor(deviceId);
+        for (size_t i = 0; i < connections.size(); ++i) {
+            removeStream(deviceId, connections.keyAt(i));
+        }
+        connections.clear();
+        mConnections.removeItem(deviceId);
     }
-    connections.clear();
-    mConnections.removeItem(deviceId);
+    JNIEnv* env = AndroidRuntime::getJNIEnv();
     env->CallVoidMethod(
             mThiz,
             gTvInputHalClassInfo.deviceUnavailable,
@@ -304,18 +511,36 @@
 }
 
 void JTvInputHal::onStreamConfigurationsChanged(int deviceId) {
-    JNIEnv* env = AndroidRuntime::getJNIEnv();
-    KeyedVector<int, Connection>& connections = mConnections.editValueFor(deviceId);
-    for (size_t i = 0; i < connections.size(); ++i) {
-        removeStream(deviceId, connections.keyAt(i));
+    {
+        Mutex::Autolock autoLock(&mLock);
+        KeyedVector<int, Connection>& connections = mConnections.editValueFor(deviceId);
+        for (size_t i = 0; i < connections.size(); ++i) {
+            removeStream(deviceId, connections.keyAt(i));
+        }
+        connections.clear();
     }
-    connections.clear();
+    JNIEnv* env = AndroidRuntime::getJNIEnv();
     env->CallVoidMethod(
             mThiz,
             gTvInputHalClassInfo.streamConfigsChanged,
             deviceId);
 }
 
+void JTvInputHal::onCaptured(int deviceId, int streamId, uint32_t seq, bool succeeded) {
+    sp<BufferProducerThread> thread;
+    {
+        Mutex::Autolock autoLock(&mLock);
+        KeyedVector<int, Connection>& connections = mConnections.editValueFor(deviceId);
+        Connection& connection = connections.editValueFor(streamId);
+        if (connection.mThread == NULL) {
+            ALOGE("capture thread not existing.");
+            return;
+        }
+        thread = connection.mThread;
+    }
+    thread->onCaptured(seq, succeeded);
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 
 static jlong nativeOpen(JNIEnv* env, jobject thiz) {
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/DatabaseHelper.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/DatabaseHelper.java
new file mode 100644
index 0000000..a1240f4
--- /dev/null
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/DatabaseHelper.java
@@ -0,0 +1,181 @@
+/**
+ * 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.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteDatabase.CursorFactory;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.hardware.soundtrigger.SoundTrigger;
+import android.hardware.soundtrigger.SoundTrigger.Keyphrase;
+import android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel;
+import android.util.Slog;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+
+/**
+ * @hide
+ */
+public class DatabaseHelper extends SQLiteOpenHelper {
+    static final String TAG = "SoundModelDBHelper";
+
+    private static final String NAME = "sound_model.db";
+    private static final int VERSION = 1;
+
+    public static interface KeyphraseContract {
+        public static final String TABLE = "keyphrase";
+        public static final String KEY_ID = "_id";
+        public static final String KEY_RECOGNITION_MODES = "modes";
+        public static final String KEY_LOCALE = "locale";
+        public static final String KEY_HINT_TEXT = "hint_text";
+        public static final String KEY_USERS = "users";
+        public static final String KEY_SOUND_MODEL_ID = "sound_model_id";
+    }
+
+    public static interface SoundModelContract {
+        public static final String TABLE = "sound_model";
+        public static final String KEY_ID = "_id";
+        public static final String KEY_TYPE = "type";
+        public static final String KEY_DATA = "data";
+    }
+
+    // Table Create Statements
+    private static final String CREATE_TABLE_KEYPRHASES = "CREATE TABLE "
+            + KeyphraseContract.TABLE + "("
+            + KeyphraseContract.KEY_ID + " INTEGER PRIMARY KEY,"
+            + KeyphraseContract.KEY_RECOGNITION_MODES + " INTEGER,"
+            + KeyphraseContract.KEY_USERS + " INTEGER,"
+            + KeyphraseContract.KEY_SOUND_MODEL_ID + " TEXT,"
+            + KeyphraseContract.KEY_LOCALE + " TEXT,"
+            + KeyphraseContract.KEY_HINT_TEXT + " TEXT" + ")";
+
+    private static final String CREATE_TABLE_SOUND_MODEL = "CREATE TABLE "
+            + SoundModelContract.TABLE + "("
+            + SoundModelContract.KEY_ID + " TEXT PRIMARY KEY,"
+            + SoundModelContract.KEY_TYPE + " INTEGER,"
+            + SoundModelContract.KEY_DATA + " BLOB" + ")";
+
+    public DatabaseHelper(Context context, CursorFactory factory) {
+        super(context, NAME, null, VERSION);
+    }
+
+    @Override
+    public void onCreate(SQLiteDatabase db) {
+        // creating required tables
+        db.execSQL(CREATE_TABLE_KEYPRHASES);
+        db.execSQL(CREATE_TABLE_SOUND_MODEL);
+    }
+
+    @Override
+    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+        // TODO(sansid): For now, drop older tables and recreate new ones.
+        db.execSQL("DROP TABLE IF EXISTS " + KeyphraseContract.TABLE);
+        db.execSQL("DROP TABLE IF EXISTS " + SoundModelContract.TABLE);
+        onCreate(db);
+    }
+
+    /**
+     * TODO(sansid): Change to addOrUpdate to handle changes here.
+     */
+    public void addKeyphraseSoundModel(KeyphraseSoundModel soundModel) {
+        SQLiteDatabase db = this.getWritableDatabase();
+        ContentValues values = new ContentValues();
+        // Generate a random ID for the model.
+        values.put(SoundModelContract.KEY_ID, soundModel.uuid.toString());
+        values.put(SoundModelContract.KEY_DATA, soundModel.data);
+        values.put(SoundModelContract.KEY_TYPE, SoundTrigger.SoundModel.TYPE_KEYPHRASE);
+
+        if (db.insert(SoundModelContract.TABLE, null, values) != -1) {
+            for (Keyphrase keyphrase : soundModel.keyphrases) {
+                addKeyphrase(soundModel.uuid, keyphrase);
+            }
+        } else {
+            Slog.w(TAG, "Failed to persist sound model to database");
+        }
+    }
+
+    /**
+     * TODO(sansid): Change to addOrUpdate to handle changes here.
+     */
+    private void addKeyphrase(UUID modelId, SoundTrigger.Keyphrase keyphrase) {
+        SQLiteDatabase db = this.getWritableDatabase();
+        ContentValues values = new ContentValues();
+        values.put(KeyphraseContract.KEY_ID, keyphrase.id);
+        values.put(KeyphraseContract.KEY_RECOGNITION_MODES, keyphrase.recognitionModes);
+        values.put(KeyphraseContract.KEY_SOUND_MODEL_ID, keyphrase.id);
+        values.put(KeyphraseContract.KEY_HINT_TEXT, keyphrase.text);
+        values.put(KeyphraseContract.KEY_LOCALE, keyphrase.locale);
+        if (db.insert(KeyphraseContract.TABLE, null, values) == -1) {
+            Slog.w(TAG, "Failed to persist keyphrase to database");
+        }
+    }
+
+    /**
+     * Lists all the keyphrase sound models currently registered with the system.
+     */
+    public List<KeyphraseSoundModel> getKephraseSoundModels() {
+        List<KeyphraseSoundModel> models = new ArrayList<>();
+        String selectQuery = "SELECT  * FROM " + SoundModelContract.TABLE;
+        SQLiteDatabase db = this.getReadableDatabase();
+        Cursor c = db.rawQuery(selectQuery, null);
+
+        // looping through all rows and adding to list
+        if (c.moveToFirst()) {
+            do {
+                int type = c.getInt(c.getColumnIndex(SoundModelContract.KEY_TYPE));
+                if (type != SoundTrigger.SoundModel.TYPE_KEYPHRASE) {
+                    // Ignore non-keyphrase sound models.
+                    continue;
+                }
+                String id = c.getString(c.getColumnIndex(SoundModelContract.KEY_ID));
+                byte[] data = c.getBlob(c.getColumnIndex(SoundModelContract.KEY_DATA));
+                // Get all the keyphrases for this this sound model.
+                models.add(new KeyphraseSoundModel(
+                        UUID.fromString(id), data, getKeyphrasesForSoundModel(id)));
+            } while (c.moveToNext());
+        }
+        return models;
+    }
+
+    private Keyphrase[] getKeyphrasesForSoundModel(String modelId) {
+        List<Keyphrase> keyphrases = new ArrayList<>();
+        String selectQuery = "SELECT  * FROM " + KeyphraseContract.TABLE
+                + " WHERE " + KeyphraseContract.KEY_SOUND_MODEL_ID + " = '" + modelId + "'";
+        SQLiteDatabase db = this.getReadableDatabase();
+        Cursor c = db.rawQuery(selectQuery, null);
+
+        // looping through all rows and adding to list
+        if (c.moveToFirst()) {
+            do {
+                int id = c.getInt(c.getColumnIndex(KeyphraseContract.KEY_ID));
+                int modes = c.getInt(c.getColumnIndex(KeyphraseContract.KEY_RECOGNITION_MODES));
+                int[] users = {c.getInt(c.getColumnIndex(KeyphraseContract.KEY_USERS))};
+                String locale = c.getString(c.getColumnIndex(KeyphraseContract.KEY_LOCALE));
+                String hintText = c.getString(c.getColumnIndex(KeyphraseContract.KEY_HINT_TEXT));
+
+                keyphrases.add(new Keyphrase(id, modes, locale, hintText, users));
+            } while (c.moveToNext());
+        }
+        Keyphrase[] keyphraseArr = new Keyphrase[keyphrases.size()];
+        keyphrases.toArray(keyphraseArr);
+        return keyphraseArr;
+    }
+}
diff --git a/telecomm/java/android/telecomm/ConnectionRequest.java b/telecomm/java/android/telecomm/ConnectionRequest.java
index ed61622..28fea79 100644
--- a/telecomm/java/android/telecomm/ConnectionRequest.java
+++ b/telecomm/java/android/telecomm/ConnectionRequest.java
@@ -34,20 +34,19 @@
     private final Uri mHandle;
     private final Bundle mExtras;
     private final PhoneAccount mAccount;
+    private final int mVideoState;
 
-    public ConnectionRequest(Uri handle, Bundle extras) {
-        this(null, handle, extras);
+    public ConnectionRequest(String callId, Uri handle, Bundle extras, int videoState) {
+        this(null, callId, handle, extras, videoState);
     }
 
-    public ConnectionRequest(String callId, Uri handle, Bundle extras) {
-        this(null, callId, handle, extras);
-    }
-
-    public ConnectionRequest(PhoneAccount account, String callId, Uri handle, Bundle extras) {
+    public ConnectionRequest(PhoneAccount account, String callId, Uri handle, Bundle extras,
+            int videoState) {
         mCallId = callId;
         mHandle = handle;
         mExtras = extras;
         mAccount = account;
+        mVideoState = videoState;
     }
 
     /**
@@ -72,6 +71,19 @@
      */
     public Bundle getExtras() { return mExtras; }
 
+    /**
+     * Determines the video state for the connection.
+     * Valid values: {@link VideoCallProfile#VIDEO_STATE_AUDIO_ONLY},
+     * {@link VideoCallProfile#VIDEO_STATE_BIDIRECTIONAL},
+     * {@link VideoCallProfile#VIDEO_STATE_TX_ENABLED},
+     * {@link VideoCallProfile#VIDEO_STATE_RX_ENABLED}.
+     *
+     * @return The video state for the connection.
+     */
+    public int getVideoState() {
+        return mVideoState;
+    }
+
     public String toString() {
         return String.format("PhoneConnectionRequest %s %s",
                 mHandle == null
@@ -87,7 +99,8 @@
                     String callId = source.readString();
                     Uri handle = (Uri) source.readParcelable(getClass().getClassLoader());
                     Bundle extras = (Bundle) source.readParcelable(getClass().getClassLoader());
-                    return new ConnectionRequest(callId, handle, extras);
+                    int videoState = source.readInt();
+                    return new ConnectionRequest(callId, handle, extras, videoState);
                 }
 
                 @Override
@@ -109,4 +122,6 @@
         destination.writeString(mCallId);
         destination.writeParcelable(mHandle, 0);
         destination.writeParcelable(mExtras, 0);
-    }}
+        destination.writeInt(mVideoState);
+    }
+}
diff --git a/telecomm/java/android/telecomm/RemoteConnectionService.java b/telecomm/java/android/telecomm/RemoteConnectionService.java
index 73e7d77..4f4941d 100644
--- a/telecomm/java/android/telecomm/RemoteConnectionService.java
+++ b/telecomm/java/android/telecomm/RemoteConnectionService.java
@@ -229,7 +229,7 @@
         if (mConnectionId == null) {
             String id = UUID.randomUUID().toString();
             ConnectionRequest newRequest = new ConnectionRequest(request.getAccount(), id,
-                    request.getHandle(), request.getExtras());
+                    request.getHandle(), request.getExtras(), request.getVideoState());
             try {
                 mConnectionService.call(newRequest);
                 mConnectionId = id;
diff --git a/telecomm/java/android/telecomm/TelecommConstants.java b/telecomm/java/android/telecomm/TelecommConstants.java
index 513a5ee..b9fb40c 100644
--- a/telecomm/java/android/telecomm/TelecommConstants.java
+++ b/telecomm/java/android/telecomm/TelecommConstants.java
@@ -58,11 +58,15 @@
             "android.intent.extra.START_CALL_WITH_SPEAKERPHONE";
 
     /**
-     * Optional extra for {@link Intent#ACTION_CALL} containing a boolean that determines whether
-     * the call should be started with video, if possible.
+     * Optional extra for {@link Intent#ACTION_CALL} containing an integer that determines the
+     * desired video state for an outgoing call.
+     * Valid options: {@link VideoCallProfile#VIDEO_STATE_AUDIO_ONLY},
+     * {@link VideoCallProfile#VIDEO_STATE_BIDIRECTIONAL},
+     * {@link VideoCallProfile#VIDEO_STATE_RX_ENABLED},
+     * {@link VideoCallProfile#VIDEO_STATE_TX_ENABLED}.
      */
-    public static final String EXTRA_START_CALL_WITH_VIDEO =
-            "android.intent.extra.START_CALL_WITH_VIDEO";
+    public static final String EXTRA_START_CALL_WITH_VIDEO_STATE =
+            "android.intent.extra.START_CALL_WITH_VIDEO_STATE";
 
     /**
      * Extra for {@link #ACTION_INCOMING_CALL} containing the {@link CallServiceDescriptor} that
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index b29cc12..e4885a1 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -2300,7 +2300,7 @@
      * Input parameters equivalent to TS 27.007 AT+CCHO command.
      *
      * <p>Requires Permission:
-     *   {@link android.Manifest.permission#SIM_COMMUNICATION SIM_COMMUNICATION}
+     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
      *
      * @param AID Application id. See ETSI 102.221 and 101.220.
      * @return The logical channel id which is negative on error.
@@ -2322,7 +2322,7 @@
      * Input parameters equivalent to TS 27.007 AT+CCHC command.
      *
      * <p>Requires Permission:
-     *   {@link android.Manifest.permission#SIM_COMMUNICATION SIM_COMMUNICATION}
+     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
      *
      * @param channel is the channel id to be closed as retruned by a successful
      *            iccOpenLogicalChannel.
@@ -2345,7 +2345,7 @@
      * Input parameters equivalent to TS 27.007 AT+CGLA command.
      *
      * <p>Requires Permission:
-     *   {@link android.Manifest.permission#SIM_COMMUNICATION SIM_COMMUNICATION}
+     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
      *
      * @param channel is the channel id to be closed as returned by a successful
      *            iccOpenLogicalChannel.
@@ -2376,7 +2376,7 @@
      * Send ENVELOPE to the SIM and return the response.
      *
      * <p>Requires Permission:
-     *   {@link android.Manifest.permission#SIM_COMMUNICATION SIM_COMMUNICATION}
+     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
      *
      * @param content String containing SAT/USAT response in hexadecimal
      *                format starting with command tag. See TS 102 223 for
@@ -2756,6 +2756,25 @@
     }
 
     /**
+     * Get the calculated preferred network type.
+     * Used for debugging incorrect network type.
+     *
+     * @return the preferred network type, defined in RILConstants.java or -1 if
+     *         none available.
+     * @hide
+     */
+    public int getCalculatedPreferredNetworkType() {
+        try {
+            return getITelephony().getCalculatedPreferredNetworkType();
+        } catch (RemoteException ex) {
+            Rlog.e(TAG, "getCalculatedPreferredNetworkType RemoteException", ex);
+        } catch (NullPointerException ex) {
+            Rlog.e(TAG, "getCalculatedPreferredNetworkType NPE", ex);
+        }
+        return -1;
+    }
+
+    /**
      * Get the preferred network type.
      * Used for device configuration by some CDMA operators.
      *
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 5b6db4d..7bf3b3f 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -584,6 +584,14 @@
     boolean nvResetConfig(int resetType);
 
     /*
+     * Get the calculated preferred network type.
+     * Used for device configuration by some CDMA operators.
+     *
+     * @return the calculated preferred network type, defined in RILConstants.java.
+     */
+    int getCalculatedPreferredNetworkType();
+
+    /*
      * Get the preferred network type.
      * Used for device configuration by some CDMA operators.
      *
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index a30fb79..ae16102 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -1447,6 +1447,14 @@
      */
     public static final int INVALID_ARGS                = 8;
 
+    /**
+     * Passed with {@link ActionListener#onFailure}.
+     * Indicates that the operation failed due to user permissions.
+     *
+     * @hide
+     */
+    public static final int NOT_AUTHORIZED              = 9;
+
     /** Interface for callback invocation on an application action */
     public interface ActionListener {
         /** The operation succeeded */
diff --git a/wifi/java/android/net/wifi/passpoint/IWifiPasspointManager.aidl b/wifi/java/android/net/wifi/passpoint/IWifiPasspointManager.aidl
index 61c2b8a..50bec33 100644
--- a/wifi/java/android/net/wifi/passpoint/IWifiPasspointManager.aidl
+++ b/wifi/java/android/net/wifi/passpoint/IWifiPasspointManager.aidl
@@ -18,6 +18,7 @@
 
 import android.net.wifi.ScanResult;
 import android.net.wifi.passpoint.WifiPasspointPolicy;
+import android.net.wifi.passpoint.WifiPasspointCredential;
 import android.os.Messenger;
 
 /**
@@ -28,7 +29,17 @@
 interface IWifiPasspointManager
 {
     Messenger getMessenger();
+
     int getPasspointState();
+
     List<WifiPasspointPolicy> requestCredentialMatch(in List<ScanResult> requested);
+
+    List<WifiPasspointCredential> getCredentials();
+
+    boolean addCredential(in WifiPasspointCredential cred);
+
+    boolean updateCredential(in WifiPasspointCredential cred);
+
+    boolean removeCredential(in WifiPasspointCredential cred);
 }
 
diff --git a/wifi/java/android/net/wifi/passpoint/WifiPasspointCredential.java b/wifi/java/android/net/wifi/passpoint/WifiPasspointCredential.java
index 33ccad5..0a7230f 100644
--- a/wifi/java/android/net/wifi/passpoint/WifiPasspointCredential.java
+++ b/wifi/java/android/net/wifi/passpoint/WifiPasspointCredential.java
@@ -333,7 +333,7 @@
      * Set the fully qualified domain name (FQDN) of this Passpoint credential.
      * @param fqdn FQDN
      */
-    public void setFqdn(String fqdn) {
+    public void setHomeFqdn(String fqdn) {
         mHomeSpFqdn = fqdn;
     }
 
diff --git a/wifi/java/android/net/wifi/passpoint/WifiPasspointManager.java b/wifi/java/android/net/wifi/passpoint/WifiPasspointManager.java
index ddca85e..b9b17eb 100644
--- a/wifi/java/android/net/wifi/passpoint/WifiPasspointManager.java
+++ b/wifi/java/android/net/wifi/passpoint/WifiPasspointManager.java
@@ -496,7 +496,11 @@
      * @return The list of credentials
      */
     public List<WifiPasspointCredential> getCredentials() {
-        return null;
+        try {
+            return mService.getCredentials();
+        } catch (RemoteException e) {
+            return null;
+        }
     }
 
     /**
@@ -506,7 +510,11 @@
      * @return {@code true} if the operation succeeds, {@code false} otherwise
      */
     public boolean addCredential(WifiPasspointCredential cred) {
-        return true;
+        try {
+            return mService.addCredential(cred);
+        } catch (RemoteException e) {
+            return false;
+        }
     }
 
     /**
@@ -517,7 +525,11 @@
      * @return {@code true} if the operation succeeds, {@code false} otherwise
      */
     public boolean updateCredential(WifiPasspointCredential cred) {
-        return true;
+        try {
+            return mService.updateCredential(cred);
+        } catch (RemoteException e) {
+            return false;
+        }
     }
 
     /**
@@ -528,7 +540,11 @@
      * @return {@code true} if the operation succeeds, {@code false} otherwise
      */
     public boolean removeCredential(WifiPasspointCredential cred) {
-        return true;
+        try {
+            return mService.removeCredential(cred);
+        } catch (RemoteException e) {
+            return false;
+        }
     }
 
     public void startOsu(Channel c, WifiPasspointOsuProvider osu, OsuRemListener listener) {