Merge "Syncronize renderPage vs. onServiceDisconnected" into nyc-mr1-dev
diff --git a/api/current.txt b/api/current.txt
index f7b15ba..c631627 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -36768,9 +36768,11 @@
     field public static final java.lang.String ACTION_CARRIER_CONFIG_CHANGED = "android.telephony.action.CARRIER_CONFIG_CHANGED";
     field public static final java.lang.String KEY_ADDITIONAL_CALL_SETTING_BOOL = "additional_call_setting_bool";
     field public static final java.lang.String KEY_ALLOW_ADDING_APNS_BOOL = "allow_adding_apns_bool";
+    field public static final java.lang.String KEY_ALLOW_ADD_CALL_DURING_VIDEO_CALL_BOOL = "allow_add_call_during_video_call";
     field public static final java.lang.String KEY_ALLOW_EMERGENCY_NUMBERS_IN_CALL_LOG_BOOL = "allow_emergency_numbers_in_call_log_bool";
     field public static final java.lang.String KEY_ALLOW_EMERGENCY_VIDEO_CALLS_BOOL = "allow_emergency_video_calls_bool";
     field public static final java.lang.String KEY_ALLOW_LOCAL_DTMF_TONES_BOOL = "allow_local_dtmf_tones_bool";
+    field public static final java.lang.String KEY_ALLOW_MERGE_WIFI_CALLS_WHEN_VOWIFI_OFF_BOOL = "allow_merge_wifi_calls_when_vowifi_off_bool";
     field public static final java.lang.String KEY_ALLOW_NON_EMERGENCY_CALLS_IN_ECM_BOOL = "allow_non_emergency_calls_in_ecm_bool";
     field public static final java.lang.String KEY_ALWAYS_SHOW_EMERGENCY_ALERT_ONOFF_BOOL = "always_show_emergency_alert_onoff_bool";
     field public static final java.lang.String KEY_APN_EXPAND_BOOL = "apn_expand_bool";
diff --git a/api/system-current.txt b/api/system-current.txt
index bf1842e..3fa518a 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -39807,9 +39807,11 @@
     field public static final java.lang.String ACTION_CARRIER_CONFIG_CHANGED = "android.telephony.action.CARRIER_CONFIG_CHANGED";
     field public static final java.lang.String KEY_ADDITIONAL_CALL_SETTING_BOOL = "additional_call_setting_bool";
     field public static final java.lang.String KEY_ALLOW_ADDING_APNS_BOOL = "allow_adding_apns_bool";
+    field public static final java.lang.String KEY_ALLOW_ADD_CALL_DURING_VIDEO_CALL_BOOL = "allow_add_call_during_video_call";
     field public static final java.lang.String KEY_ALLOW_EMERGENCY_NUMBERS_IN_CALL_LOG_BOOL = "allow_emergency_numbers_in_call_log_bool";
     field public static final java.lang.String KEY_ALLOW_EMERGENCY_VIDEO_CALLS_BOOL = "allow_emergency_video_calls_bool";
     field public static final java.lang.String KEY_ALLOW_LOCAL_DTMF_TONES_BOOL = "allow_local_dtmf_tones_bool";
+    field public static final java.lang.String KEY_ALLOW_MERGE_WIFI_CALLS_WHEN_VOWIFI_OFF_BOOL = "allow_merge_wifi_calls_when_vowifi_off_bool";
     field public static final java.lang.String KEY_ALLOW_NON_EMERGENCY_CALLS_IN_ECM_BOOL = "allow_non_emergency_calls_in_ecm_bool";
     field public static final java.lang.String KEY_ALWAYS_SHOW_EMERGENCY_ALERT_ONOFF_BOOL = "always_show_emergency_alert_onoff_bool";
     field public static final java.lang.String KEY_APN_EXPAND_BOOL = "apn_expand_bool";
diff --git a/api/test-current.txt b/api/test-current.txt
index fa06622..993f388 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -36846,9 +36846,11 @@
     field public static final java.lang.String ACTION_CARRIER_CONFIG_CHANGED = "android.telephony.action.CARRIER_CONFIG_CHANGED";
     field public static final java.lang.String KEY_ADDITIONAL_CALL_SETTING_BOOL = "additional_call_setting_bool";
     field public static final java.lang.String KEY_ALLOW_ADDING_APNS_BOOL = "allow_adding_apns_bool";
+    field public static final java.lang.String KEY_ALLOW_ADD_CALL_DURING_VIDEO_CALL_BOOL = "allow_add_call_during_video_call";
     field public static final java.lang.String KEY_ALLOW_EMERGENCY_NUMBERS_IN_CALL_LOG_BOOL = "allow_emergency_numbers_in_call_log_bool";
     field public static final java.lang.String KEY_ALLOW_EMERGENCY_VIDEO_CALLS_BOOL = "allow_emergency_video_calls_bool";
     field public static final java.lang.String KEY_ALLOW_LOCAL_DTMF_TONES_BOOL = "allow_local_dtmf_tones_bool";
+    field public static final java.lang.String KEY_ALLOW_MERGE_WIFI_CALLS_WHEN_VOWIFI_OFF_BOOL = "allow_merge_wifi_calls_when_vowifi_off_bool";
     field public static final java.lang.String KEY_ALLOW_NON_EMERGENCY_CALLS_IN_ECM_BOOL = "allow_non_emergency_calls_in_ecm_bool";
     field public static final java.lang.String KEY_ALWAYS_SHOW_EMERGENCY_ALERT_ONOFF_BOOL = "always_show_emergency_alert_onoff_bool";
     field public static final java.lang.String KEY_APN_EXPAND_BOOL = "apn_expand_bool";
diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index c6834f9..32a8088 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -922,6 +922,8 @@
                 flags |= UserInfo.FLAG_EPHEMERAL;
             } else if ("--guest".equals(opt)) {
                 flags |= UserInfo.FLAG_GUEST;
+            } else if ("--demo".equals(opt)) {
+                flags |= UserInfo.FLAG_DEMO;
             } else {
                 System.err.println("Error: unknown option " + opt);
                 return showUsage();
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index eaea989..05f49c5 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -39,6 +39,7 @@
 import android.media.session.MediaSession;
 import android.net.Uri;
 import android.os.BadParcelableException;
+import android.os.BaseBundle;
 import android.os.Build;
 import android.os.Bundle;
 import android.os.Parcel;
@@ -53,6 +54,7 @@
 import android.text.style.CharacterStyle;
 import android.text.style.RelativeSizeSpan;
 import android.text.style.TextAppearanceSpan;
+import android.util.ArraySet;
 import android.util.Log;
 import android.util.SparseArray;
 import android.view.Gravity;
@@ -63,6 +65,7 @@
 import android.widget.RemoteViews;
 
 import com.android.internal.R;
+import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.NotificationColorUtil;
 
 import java.lang.annotation.Retention;
@@ -70,6 +73,7 @@
 import java.lang.reflect.Constructor;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
 import java.util.Set;
@@ -758,6 +762,16 @@
     public Bundle extras = new Bundle();
 
     /**
+     * All pending intents in the notification extras (notification extras, actions extras,
+     * and remote input extras) as the system needs to be able to access them but touching
+     * the extras bundle in the system process is not safe because the bundle may contain
+     * custom parcelable objects.
+     *
+     * @hide
+     */
+    public ArraySet<PendingIntent> extrasPendingIntents;
+
+    /**
      * {@link #extras} key: this is the title of the notification,
      * as supplied to {@link Builder#setContentTitle(CharSequence)}.
      */
@@ -1573,7 +1587,16 @@
     /**
      * Unflatten the notification from a parcel.
      */
-    public Notification(Parcel parcel)
+    @SuppressWarnings("unchecked")
+    public Notification(Parcel parcel) {
+        // IMPORTANT: Add unmarshaling code in readFromParcel as the pending
+        // intents in extras are always written as the last entry.
+        readFromParcelImpl(parcel);
+        // Must be read last!
+        extrasPendingIntents = (ArraySet<PendingIntent>) parcel.readArraySet(null);
+    }
+
+    private void readFromParcelImpl(Parcel parcel)
     {
         int version = parcel.readInt();
 
@@ -1728,6 +1751,10 @@
             }
         }
 
+        if (!ArrayUtils.isEmpty(extrasPendingIntents)) {
+            that.extrasPendingIntents = new ArraySet<>(extrasPendingIntents);
+        }
+
         if (this.actions != null) {
             that.actions = new Action[this.actions.length];
             for(int i=0; i<this.actions.length; i++) {
@@ -1843,8 +1870,40 @@
     /**
      * Flatten this notification into a parcel.
      */
-    public void writeToParcel(Parcel parcel, int flags)
-    {
+    public void writeToParcel(Parcel parcel, int flags) {
+        // We need to mark all pending intents getting into the notification
+        // system as being put there to later allow the notification ranker
+        // to launch them and by doing so add the app to the battery saver white
+        // list for a short period of time. The problem is that the system
+        // cannot look into the extras as there may be parcelables there that
+        // the platform does not know how to handle. To go around that we have
+        // an explicit list of the pending intents in the extras bundle.
+        final boolean collectPendingIntents = (extrasPendingIntents == null);
+        if (collectPendingIntents) {
+            PendingIntent.setOnMarshaledListener(
+                    (PendingIntent intent, Parcel out, int outFlags) -> {
+                if (parcel == out) {
+                    if (extrasPendingIntents == null) {
+                        extrasPendingIntents = new ArraySet<>();
+                    }
+                    extrasPendingIntents.add(intent);
+                }
+            });
+        }
+        try {
+            // IMPORTANT: Add marshaling code in writeToParcelImpl as we
+            // want to intercept all pending events written to the pacel.
+            writeToParcelImpl(parcel, flags);
+            // Must be written last!
+            parcel.writeArraySet(extrasPendingIntents);
+        } finally {
+            if (collectPendingIntents) {
+                PendingIntent.setOnMarshaledListener(null);
+            }
+        }
+    }
+
+    private void writeToParcelImpl(Parcel parcel, int flags) {
         parcel.writeInt(1);
 
         parcel.writeLong(when);
diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java
index cb15392..cfa242b 100644
--- a/core/java/android/app/PendingIntent.java
+++ b/core/java/android/app/PendingIntent.java
@@ -242,6 +242,36 @@
     }
 
     /**
+     * Listener for observing when pending intents are written to a parcel.
+     *
+     * @hide
+     */
+    public interface OnMarshaledListener {
+        /**
+         * Called when a pending intent is written to a parcel.
+         *
+         * @param intent The pending intent.
+         * @param parcel The parcel to which it was written.
+         * @param flags The parcel flags when it was written.
+         */
+        void onMarshaled(PendingIntent intent, Parcel parcel, int flags);
+    }
+
+    private static final ThreadLocal<OnMarshaledListener> sOnMarshaledListener
+            = new ThreadLocal<>();
+
+    /**
+     * Registers an listener for pending intents being written to a parcel.
+     *
+     * @param listener The listener, null to clear.
+     *
+     * @hide
+     */
+    public static void setOnMarshaledListener(OnMarshaledListener listener) {
+        sOnMarshaledListener.set(listener);
+    }
+
+    /**
      * Retrieve a PendingIntent that will start a new activity, like calling
      * {@link Context#startActivity(Intent) Context.startActivity(Intent)}.
      * Note that the activity will be started outside of the context of an
@@ -1016,6 +1046,11 @@
 
     public void writeToParcel(Parcel out, int flags) {
         out.writeStrongBinder(mTarget.asBinder());
+        OnMarshaledListener listener = sOnMarshaledListener.get();
+        if (listener != null) {
+            listener.onMarshaled(this, out, flags);
+        }
+
     }
 
     public static final Parcelable.Creator<PendingIntent> CREATOR
diff --git a/core/java/android/content/pm/IOtaDexopt.aidl b/core/java/android/content/pm/IOtaDexopt.aidl
index 786a77f..467bd5f 100644
--- a/core/java/android/content/pm/IOtaDexopt.aidl
+++ b/core/java/android/content/pm/IOtaDexopt.aidl
@@ -50,6 +50,13 @@
     /**
      * Optimize the next package. Note: this command is synchronous, that is, only returns after
      * the package has been dexopted (or dexopting failed).
+     *
+     * Note: this will be removed after a transition period. Use nextDexoptCommand instead.
      */
     void dexoptNextPackage();
+
+    /**
+     * Get the optimization parameters for the next package.
+     */
+    String nextDexoptCommand();
 }
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index 2631247..74dcc07 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -17,8 +17,10 @@
 package android.os;
 
 import android.annotation.IntegerRes;
+import android.annotation.Nullable;
 import android.text.TextUtils;
 import android.util.ArrayMap;
+import android.util.ArraySet;
 import android.util.Log;
 import android.util.Size;
 import android.util.SizeF;
@@ -734,6 +736,21 @@
     }
 
     /**
+     * Write an array set to the parcel.
+     *
+     * @param val The array set to write.
+     *
+     * @hide
+     */
+    public void writeArraySet(@Nullable ArraySet<? extends Object> val) {
+        final int size = (val != null) ? val.size() : -1;
+        writeInt(size);
+        for (int i = 0; i < size; i++) {
+            writeValue(val.valueAt(i));
+        }
+    }
+
+    /**
      * Flatten a Bundle into the parcel at the current dataPosition(),
      * growing dataCapacity() if needed.
      */
@@ -2735,6 +2752,26 @@
         readArrayMapInternal(outVal, N, loader);
     }
 
+    /**
+     * Reads an array set.
+     *
+     * @param loader The class loader to use.
+     *
+     * @hide
+     */
+    public @Nullable ArraySet<? extends Object> readArraySet(ClassLoader loader) {
+        final int size = readInt();
+        if (size < 0) {
+            return null;
+        }
+        ArraySet<Object> result = new ArraySet<>(size);
+        for (int i = 0; i < size; i++) {
+            Object value = readValue(loader);
+            result.append(value);
+        }
+        return result;
+    }
+
     private void readListInternal(List outVal, int N,
         ClassLoader loader) {
         while (N > 0) {
diff --git a/core/java/android/util/ArraySet.java b/core/java/android/util/ArraySet.java
index 9e9314f..d39e91f 100644
--- a/core/java/android/util/ArraySet.java
+++ b/core/java/android/util/ArraySet.java
@@ -390,6 +390,32 @@
     }
 
     /**
+     * Special fast path for appending items to the end of the array without validation.
+     * The array must already be large enough to contain the item.
+     * @hide
+     */
+    public void append(E value) {
+        final int index = mSize;
+        final int hash = value == null ? 0
+                : (mIdentityHashCode ? System.identityHashCode(value) : value.hashCode());
+        if (index >= mHashes.length) {
+            throw new IllegalStateException("Array is full");
+        }
+        if (index > 0 && mHashes[index - 1] > hash) {
+            RuntimeException e = new RuntimeException("here");
+            e.fillInStackTrace();
+            Log.w(TAG, "New hash " + hash
+                    + " is before end of array hash " + mHashes[index - 1]
+                    + " at index " + index, e);
+            add(value);
+            return;
+        }
+        mSize = index + 1;
+        mHashes[index] = hash;
+        mArray[index] = value;
+    }
+
+    /**
      * Perform a {@link #add(Object)} of all values in <var>array</var>
      * @param array The array whose contents are to be retrieved.
      */
diff --git a/core/java/com/android/internal/app/PlatLogoActivity.java b/core/java/com/android/internal/app/PlatLogoActivity.java
index bddd826..36ab394 100644
--- a/core/java/com/android/internal/app/PlatLogoActivity.java
+++ b/core/java/com/android/internal/app/PlatLogoActivity.java
@@ -54,6 +54,7 @@
 
 public class PlatLogoActivity extends Activity {
     public static final boolean REVEAL_THE_NAME = false;
+    public static final boolean FINISH = false;
 
     FrameLayout mLayout;
     int mTapCount;
@@ -138,7 +139,7 @@
                                 } catch (ActivityNotFoundException ex) {
                                     Log.e("PlatLogoActivity", "No more eggs.");
                                 }
-                                finish();
+                                if (FINISH) finish();
                             }
                         });
                         return true;
diff --git a/core/jni/android_view_RenderNode.cpp b/core/jni/android_view_RenderNode.cpp
index 4fc546c..b0028e1 100644
--- a/core/jni/android_view_RenderNode.cpp
+++ b/core/jni/android_view_RenderNode.cpp
@@ -573,8 +573,9 @@
                 bounds.roundOut();
             }
 
+            incStrong(0);
             auto functor = std::bind(
-                std::mem_fn(&SurfaceViewPositionUpdater::doUpdatePosition), this,
+                std::mem_fn(&SurfaceViewPositionUpdater::doUpdatePositionAsync), this,
                 (jlong) info.canvasContext.getFrameNumber(),
                 (jint) bounds.left, (jint) bounds.top,
                 (jint) bounds.right, (jint) bounds.bottom);
@@ -585,15 +586,18 @@
         virtual void onPositionLost(RenderNode& node, const TreeInfo* info) override {
             if (CC_UNLIKELY(!mWeakRef || (info && !info->updateWindowPositions))) return;
 
-            if (info) {
-                auto functor = std::bind(
-                    std::mem_fn(&SurfaceViewPositionUpdater::doNotifyPositionLost), this,
-                    (jlong) info->canvasContext.getFrameNumber());
-
-                info->canvasContext.enqueueFrameWork(std::move(functor));
-            } else {
-                doNotifyPositionLost(0);
+            ATRACE_NAME("SurfaceView position lost");
+            JNIEnv* env = jnienv();
+            jobject localref = env->NewLocalRef(mWeakRef);
+            if (CC_UNLIKELY(!localref)) {
+                jnienv()->DeleteWeakGlobalRef(mWeakRef);
+                mWeakRef = nullptr;
+                return;
             }
+
+            env->CallVoidMethod(localref, gSurfaceViewPositionLostMethod,
+                    info ? info->canvasContext.getFrameNumber() : 0);
+            env->DeleteLocalRef(localref);
         }
 
     private:
@@ -605,36 +609,23 @@
             return env;
         }
 
-        void doUpdatePosition(jlong frameNumber, jint left, jint top,
+        void doUpdatePositionAsync(jlong frameNumber, jint left, jint top,
                 jint right, jint bottom) {
             ATRACE_NAME("Update SurfaceView position");
 
             JNIEnv* env = jnienv();
             jobject localref = env->NewLocalRef(mWeakRef);
             if (CC_UNLIKELY(!localref)) {
-                jnienv()->DeleteWeakGlobalRef(mWeakRef);
+                env->DeleteWeakGlobalRef(mWeakRef);
                 mWeakRef = nullptr;
-                return;
+            } else {
+                env->CallVoidMethod(localref, gSurfaceViewPositionUpdateMethod,
+                        frameNumber, left, top, right, bottom);
+                env->DeleteLocalRef(localref);
             }
 
-            env->CallVoidMethod(localref, gSurfaceViewPositionUpdateMethod,
-                    frameNumber, left, top, right, bottom);
-            env->DeleteLocalRef(localref);
-        }
-
-        void doNotifyPositionLost(jlong frameNumber) {
-            ATRACE_NAME("SurfaceView position lost");
-
-            JNIEnv* env = jnienv();
-            jobject localref = env->NewLocalRef(mWeakRef);
-            if (CC_UNLIKELY(!localref)) {
-                jnienv()->DeleteWeakGlobalRef(mWeakRef);
-                mWeakRef = nullptr;
-                return;
-            }
-
-            env->CallVoidMethod(localref, gSurfaceViewPositionLostMethod, frameNumber);
-            env->DeleteLocalRef(localref);
+            // We need to release ourselves here
+            decStrong(0);
         }
 
         JavaVM* mVm;
diff --git a/core/res/res/drawable-nodpi/platlogo.xml b/core/res/res/drawable-nodpi/platlogo.xml
index defa83a..516f252 100644
--- a/core/res/res/drawable-nodpi/platlogo.xml
+++ b/core/res/res/drawable-nodpi/platlogo.xml
@@ -19,10 +19,10 @@
         android:viewportWidth="48.0"
         android:viewportHeight="48.0">
     <path
-        android:fillColor="#FF7E5BBF"
+        android:fillColor="#FFc7d4b6"
         android:pathData="M32.0,12.5l0.0,28.0l12.0,-5.0l0.0,-28.0z"/>
     <path
-        android:fillColor="#FF7E5BBF"
+        android:fillColor="#FFfbd3cb"
         android:pathData="M4.0,40.5l12.0,-5.0l0.0,-11.0l-12.0,-12.0z"/>
     <path
         android:fillColor="#40000000"
@@ -31,7 +31,7 @@
         android:fillColor="#40000000"
         android:pathData="M4.0,12.5l12.0,12.0l0.0,4.0z"/>
     <path
-        android:fillColor="#FF55C4F5"
+        android:fillColor="#FFe0e0d6"
         android:pathData="M32.0,23.5l-16.0,-16.0l-12.0,5.0l0.0,0.0l12.0,12.0l16.0,16.0l12.0,-5.0l0.0,0.0z"/>
 </vector>
 
diff --git a/libs/hwui/LayerUpdateQueue.cpp b/libs/hwui/LayerUpdateQueue.cpp
index db5f676..95f5cfb 100644
--- a/libs/hwui/LayerUpdateQueue.cpp
+++ b/libs/hwui/LayerUpdateQueue.cpp
@@ -26,6 +26,7 @@
 }
 
 void LayerUpdateQueue::enqueueLayerWithDamage(RenderNode* renderNode, Rect damage) {
+    damage.roundOut();
     damage.doIntersect(0, 0, renderNode->getWidth(), renderNode->getHeight());
     if (!damage.isEmpty()) {
         for (Entry& entry : mEntries) {
diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h
index f80be5e..47fef6d 100644
--- a/libs/hwui/RenderNode.h
+++ b/libs/hwui/RenderNode.h
@@ -232,7 +232,7 @@
     // the frameNumber to appropriately batch/synchronize these transactions.
     // There is no other filtering/batching to ensure that only the "final"
     // state called once per frame.
-    class ANDROID_API PositionListener {
+    class ANDROID_API PositionListener : public VirtualLightRefBase {
     public:
         virtual ~PositionListener() {}
         // Called when the RenderNode's position changes
@@ -247,7 +247,7 @@
     // before the RenderNode is used for drawing.
     // RenderNode takes ownership of the pointer
     ANDROID_API void setPositionListener(PositionListener* listener) {
-        mPositionListener.reset(listener);
+        mPositionListener = listener;
     }
 
     // This is only modified in MODE_FULL, so it can be safely accessed
@@ -366,7 +366,7 @@
     // mDisplayList, not mStagingDisplayList.
     uint32_t mParentCount;
 
-    std::unique_ptr<PositionListener> mPositionListener;
+    sp<PositionListener> mPositionListener;
 }; // class RenderNode
 
 } /* namespace uirenderer */
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index d4956be..ceef9c7 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -784,6 +784,7 @@
     }
     sp<FuncTask> task(new FuncTask());
     task->func = func;
+    mFrameFences.push_back(task);
     mFrameWorkProcessor->add(task);
 }
 
diff --git a/libs/hwui/tests/unit/LayerUpdateQueueTests.cpp b/libs/hwui/tests/unit/LayerUpdateQueueTests.cpp
index 8b0e91c..4db1cb9 100644
--- a/libs/hwui/tests/unit/LayerUpdateQueueTests.cpp
+++ b/libs/hwui/tests/unit/LayerUpdateQueueTests.cpp
@@ -39,17 +39,21 @@
 TEST(LayerUpdateQueue, enqueueSimple) {
     sp<RenderNode> a = createSyncedNode(100, 100);
     sp<RenderNode> b = createSyncedNode(200, 200);
+    sp<RenderNode> c = createSyncedNode(200, 200);
 
     LayerUpdateQueue queue;
     queue.enqueueLayerWithDamage(a.get(), Rect(25, 25, 75, 75));
     queue.enqueueLayerWithDamage(b.get(), Rect(100, 100, 300, 300));
+    queue.enqueueLayerWithDamage(c.get(), Rect(.5, .5, .5, .5));
 
-    EXPECT_EQ(2u, queue.entries().size());
+    EXPECT_EQ(3u, queue.entries().size());
 
     EXPECT_EQ(a.get(), queue.entries()[0].renderNode);
     EXPECT_EQ(Rect(25, 25, 75, 75), queue.entries()[0].damage);
     EXPECT_EQ(b.get(), queue.entries()[1].renderNode);
     EXPECT_EQ(Rect(100, 100, 200, 200), queue.entries()[1].damage); // clipped to bounds
+    EXPECT_EQ(c.get(), queue.entries()[2].renderNode);
+    EXPECT_EQ(Rect(0, 0, 1, 1), queue.entries()[2].damage); // rounded out
 }
 
 TEST(LayerUpdateQueue, enqueueUnion) {
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index 71d1aaa..81ac594 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -297,8 +297,8 @@
  Codec-specific data in the format is automatically submitted to the codec upon {@link #start};
  you <strong>MUST NOT</strong> submit this data explicitly. If the format did not contain codec
  specific data, you can choose to submit it using the specified number of buffers in the correct
- order, according to the format requirements. Alternately, you can concatenate all codec-specific
- data and submit it as a single codec-config buffer.
+ order, according to the format requirements. In case of H.264 AVC, you can also concatenate all
+ codec-specific data and submit it as a single codec-config buffer.
  <p>
  Android uses the following codec-specific data buffers. These are also required to be set in
  the track format for proper {@link MediaMuxer} track configuration. Each parameter set and the
@@ -355,6 +355,13 @@
     <td class=NA>Not Used</td>
     <td class=NA>Not Used</td>
    </tr>
+   <tr>
+    <td>VP9</td>
+    <td>VP9 <a href="http://wiki.webmproject.org/vp9-codecprivate">CodecPrivate</a> Data
+        (optional)</td>
+    <td class=NA>Not Used</td>
+    <td class=NA>Not Used</td>
+   </tr>
   </tbody>
  </table>
 
diff --git a/packages/EasterEgg/res/drawable/icon.xml b/packages/EasterEgg/res/drawable/icon.xml
index 5e08fcb..defa83a 100644
--- a/packages/EasterEgg/res/drawable/icon.xml
+++ b/packages/EasterEgg/res/drawable/icon.xml
@@ -14,15 +14,15 @@
     limitations under the License.
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="48dp"
-        android:height="48dp"
+        android:width="512dp"
+        android:height="512dp"
         android:viewportWidth="48.0"
         android:viewportHeight="48.0">
     <path
-        android:fillColor="#00796B"
+        android:fillColor="#FF7E5BBF"
         android:pathData="M32.0,12.5l0.0,28.0l12.0,-5.0l0.0,-28.0z"/>
     <path
-        android:fillColor="#00796B"
+        android:fillColor="#FF7E5BBF"
         android:pathData="M4.0,40.5l12.0,-5.0l0.0,-11.0l-12.0,-12.0z"/>
     <path
         android:fillColor="#40000000"
@@ -31,8 +31,7 @@
         android:fillColor="#40000000"
         android:pathData="M4.0,12.5l12.0,12.0l0.0,4.0z"/>
     <path
-        android:fillColor="#4DB6AC"
+        android:fillColor="#FF55C4F5"
         android:pathData="M32.0,23.5l-16.0,-16.0l-12.0,5.0l0.0,0.0l12.0,12.0l16.0,16.0l12.0,-5.0l0.0,0.0z"/>
 </vector>
 
-
diff --git a/packages/EasterEgg/src/com/android/egg/neko/Cat.java b/packages/EasterEgg/src/com/android/egg/neko/Cat.java
index 525b035..864b20c 100644
--- a/packages/EasterEgg/src/com/android/egg/neko/Cat.java
+++ b/packages/EasterEgg/src/com/android/egg/neko/Cat.java
@@ -16,7 +16,6 @@
 
 import android.app.Notification;
 import android.app.PendingIntent;
-import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.res.Resources;
@@ -31,6 +30,8 @@
 import com.android.egg.R;
 
 public class Cat extends Drawable {
+    public static final long[] PURR = {0, 40, 20, 40, 20, 40, 20, 40, 20, 40, 20, 40};
+
     private Random mNotSoRandom;
     private Bitmap mBitmap;
     private long mSeed;
@@ -198,6 +199,7 @@
                 .setContentText(getName())
                 .setContentIntent(PendingIntent.getActivity(context, 0, intent, 0))
                 .setAutoCancel(true)
+                .setVibrate(PURR)
                 .addExtras(extras);
     }
 
diff --git a/packages/EasterEgg/src/com/android/egg/neko/NekoActivationActivity.java b/packages/EasterEgg/src/com/android/egg/neko/NekoActivationActivity.java
index 8fbab99..88a7968 100644
--- a/packages/EasterEgg/src/com/android/egg/neko/NekoActivationActivity.java
+++ b/packages/EasterEgg/src/com/android/egg/neko/NekoActivationActivity.java
@@ -18,22 +18,35 @@
 import android.content.ComponentName;
 import android.content.pm.PackageManager;
 import android.util.Log;
+import android.widget.Toast;
 
 public class NekoActivationActivity extends Activity {
+    private void toastUp(String s) {
+        Toast toast = Toast.makeText(this, s, Toast.LENGTH_SHORT);
+        toast.getView().setBackgroundDrawable(null);
+        toast.show();
+    }
+
     @Override
     public void onStart() {
+        super.onStart();
+
         final PackageManager pm = getPackageManager();
         final ComponentName cn = new ComponentName(this, NekoTile.class);
         if (pm.getComponentEnabledSetting(cn) == PackageManager.COMPONENT_ENABLED_STATE_ENABLED) {
             if (NekoLand.DEBUG) {
                 Log.v("Neko", "Disabling tile.");
             }
-            pm.setComponentEnabledSetting(cn, PackageManager.COMPONENT_ENABLED_STATE_DISABLED, 0);
+            pm.setComponentEnabledSetting(cn, PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
+                    PackageManager.DONT_KILL_APP);
+            toastUp("\uD83D\uDEAB");
         } else {
             if (NekoLand.DEBUG) {
                 Log.v("Neko", "Enabling tile.");
             }
-            pm.setComponentEnabledSetting(cn, PackageManager.COMPONENT_ENABLED_STATE_ENABLED, 0);
+            pm.setComponentEnabledSetting(cn, PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
+                    PackageManager.DONT_KILL_APP);
+            toastUp("\uD83D\uDC31");
         }
         finish();
     }
diff --git a/packages/MtpDocumentsProvider/res/values-af/strings.xml b/packages/MtpDocumentsProvider/res/values-af/strings.xml
new file mode 100644
index 0000000..c2c8761
--- /dev/null
+++ b/packages/MtpDocumentsProvider/res/values-af/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_label" msgid="6271216747302322594">"MTP-gasheer"</string>
+    <string name="downloads_app_label" msgid="7120690641874849726">"Aflaaie"</string>
+    <string name="root_name" msgid="5819495383921089536">"<xliff:g id="DEVICE_MODEL">%1$s</xliff:g> <xliff:g id="STORAGE_NAME">%2$s</xliff:g>"</string>
+    <string name="accessing_notification_title" msgid="3030133609230917944">"Toegang tot lêers word tans van <xliff:g id="DEVICE_MODEL">%1$s</xliff:g> af verkry"</string>
+    <string name="error_busy_device" msgid="3997316850357386589">"Die ander toestel is besig. Jy kan nie lêers oordra voordat dit beskikbaar is nie."</string>
+    <string name="error_locked_device" msgid="7557872102188356147">"Geen lêers is gevind nie. Die ander toestel is dalk gesluit. Indien wel, ontsluit dit en probeer weer."</string>
+</resources>
diff --git a/packages/MtpDocumentsProvider/res/values-sv/strings.xml b/packages/MtpDocumentsProvider/res/values-sv/strings.xml
new file mode 100644
index 0000000..26818eb
--- /dev/null
+++ b/packages/MtpDocumentsProvider/res/values-sv/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--  Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_label" msgid="6271216747302322594">"MTP-värd"</string>
+    <string name="downloads_app_label" msgid="7120690641874849726">"Nedladdningar"</string>
+    <string name="root_name" msgid="5819495383921089536">"<xliff:g id="DEVICE_MODEL">%1$s</xliff:g> <xliff:g id="STORAGE_NAME">%2$s</xliff:g>"</string>
+    <string name="accessing_notification_title" msgid="3030133609230917944">"Åtkomst till filer från <xliff:g id="DEVICE_MODEL">%1$s</xliff:g>"</string>
+    <string name="error_busy_device" msgid="3997316850357386589">"Den andra enheten är upptagen. Du kan inte överföra filer förrän den är tillgänglig."</string>
+    <string name="error_locked_device" msgid="7557872102188356147">"Inga filer hittades. Den andra enheten kan vara låst. Om den är det låser du upp den och försöker igen."</string>
+</resources>
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
index d3f5d26..569a567 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
@@ -82,8 +82,11 @@
     private void setTileIcon() {
         try {
             PackageManager pm = mContext.getPackageManager();
-            ServiceInfo info = pm.getServiceInfo(mComponent,
-                    PackageManager.MATCH_ENCRYPTION_AWARE_AND_UNAWARE);
+            int flags = PackageManager.MATCH_ENCRYPTION_AWARE_AND_UNAWARE;
+            if (isSystemApp(pm)) {
+                flags |= PackageManager.MATCH_DISABLED_COMPONENTS;
+            }
+            ServiceInfo info = pm.getServiceInfo(mComponent, flags);
             int icon = info.icon != 0 ? info.icon
                     : info.applicationInfo.icon;
             // Update the icon if its not set or is the default icon.
@@ -103,6 +106,10 @@
         }
     }
 
+    private boolean isSystemApp(PackageManager pm) throws PackageManager.NameNotFoundException {
+        return pm.getApplicationInfo(mComponent.getPackageName(), 0).isSystemApp();
+    }
+
     /**
      * Compare two icons, only works for resources.
      */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
index d5ff0b35..42f398d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
@@ -153,7 +153,8 @@
         if (mKeyguardUserSwitcher == null) {
             // If we have no keyguard switcher, the screen width is under 600dp. In this case,
             // we don't show the multi-user avatar unless there is more than 1 user on the device.
-            if (mUserSwitcherController.getSwitchableUserCount() > 1) {
+            if (mUserSwitcherController != null
+                    && mUserSwitcherController.getSwitchableUserCount() > 1) {
                 mMultiUserSwitch.setVisibility(View.VISIBLE);
             } else {
                 mMultiUserSwitch.setVisibility(View.GONE);
diff --git a/services/core/java/com/android/server/AttributeCache.java b/services/core/java/com/android/server/AttributeCache.java
index 57f18c0..58ec836 100644
--- a/services/core/java/com/android/server/AttributeCache.java
+++ b/services/core/java/com/android/server/AttributeCache.java
@@ -25,23 +25,24 @@
 import android.content.res.TypedArray;
 import android.os.UserHandle;
 import android.util.ArrayMap;
+import android.util.LruCache;
 import android.util.SparseArray;
 
 import com.android.internal.annotations.GuardedBy;
 
-import java.lang.ref.WeakReference;
-
 /**
  * TODO: This should be better integrated into the system so it doesn't need
  * special calls from the activity manager to clear it.
  */
 public final class AttributeCache {
+    private static final int CACHE_SIZE = 4;
     private static AttributeCache sInstance = null;
 
     private final Context mContext;
 
     @GuardedBy("this")
-    private final ArrayMap<String, WeakReference<Package>> mPackages = new ArrayMap<>();
+    private final LruCache<String, Package> mPackages = new LruCache<>(CACHE_SIZE);
+
     @GuardedBy("this")
     private final Configuration mConfiguration = new Configuration();
 
@@ -86,15 +87,12 @@
 
     public void removePackage(String packageName) {
         synchronized (this) {
-            final WeakReference<Package> ref = mPackages.remove(packageName);
-            final Package pkg = (ref != null) ? ref.get() : null;
+            final Package pkg = mPackages.remove(packageName);
             if (pkg != null) {
-                if (pkg.mMap != null) {
-                    for (int i = 0; i < pkg.mMap.size(); i++) {
-                        final ArrayMap<int[], Entry> map = pkg.mMap.valueAt(i);
-                        for (int j = 0; j < map.size(); j++) {
-                            map.valueAt(j).recycle();
-                        }
+                for (int i = 0; i < pkg.mMap.size(); i++) {
+                    final ArrayMap<int[], Entry> map = pkg.mMap.valueAt(i);
+                    for (int j = 0; j < map.size(); j++) {
+                        map.valueAt(j).recycle();
                     }
                 }
 
@@ -113,15 +111,14 @@
                 // The configurations being masked out are ones that commonly
                 // change so we don't want flushing the cache... all others
                 // will flush the cache.
-                mPackages.clear();
+                mPackages.evictAll();
             }
         }
     }
     
     public Entry get(String packageName, int resId, int[] styleable, int userId) {
         synchronized (this) {
-            WeakReference<Package> ref = mPackages.get(packageName);
-            Package pkg = (ref != null) ? ref.get() : null;
+            Package pkg = mPackages.get(packageName);
             ArrayMap<int[], Entry> map = null;
             Entry ent = null;
             if (pkg != null) {
@@ -144,7 +141,7 @@
                     return null;
                 }
                 pkg = new Package(context);
-                mPackages.put(packageName, new WeakReference<>(pkg));
+                mPackages.put(packageName, pkg);
             }
             
             if (map == null) {
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index baa0bbe..8c5887f 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -61,7 +61,7 @@
 
 class BluetoothManagerService extends IBluetoothManager.Stub {
     private static final String TAG = "BluetoothManagerService";
-    private static final boolean DBG = false;
+    private static final boolean DBG = true;
 
     private static final String BLUETOOTH_ADMIN_PERM = android.Manifest.permission.BLUETOOTH_ADMIN;
     private static final String BLUETOOTH_PERM = android.Manifest.permission.BLUETOOTH;
@@ -1512,7 +1512,7 @@
                         bluetoothStateChangeHandler(BluetoothAdapter.STATE_ON,
                                                     BluetoothAdapter.STATE_TURNING_OFF);
 
-                        waitForOnOff(false, true);
+                        boolean didDisableTimeout = !waitForOnOff(false, true);
 
                         bluetoothStateChangeHandler(BluetoothAdapter.STATE_TURNING_OFF,
                                                     BluetoothAdapter.STATE_OFF);
@@ -1530,7 +1530,16 @@
                             mBluetoothLock.writeLock().unlock();
                         }
 
-                        SystemClock.sleep(100);
+                        //
+                        // If disabling Bluetooth times out, wait for an
+                        // additional amount of time to ensure the process is
+                        // shut down completely before attempting to restart.
+                        //
+                        if (didDisableTimeout) {
+                            SystemClock.sleep(3000);
+                        } else {
+                            SystemClock.sleep(100);
+                        }
 
                         mHandler.removeMessages(MESSAGE_BLUETOOTH_STATE_CHANGE);
                         mState = BluetoothAdapter.STATE_OFF;
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 7986629..d914881 100755
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -43,7 +43,6 @@
 import android.util.ArrayMap;
 import android.util.ArraySet;
 
-import com.android.internal.app.procstats.ProcessStats;
 import com.android.internal.app.procstats.ServiceState;
 import com.android.internal.os.BatteryStatsImpl;
 import com.android.internal.os.TransferPipe;
@@ -751,6 +750,17 @@
         mAm.updateProcessForegroundLocked(proc, anyForeground, oomAdj);
     }
 
+    private void updateWhitelistManagerLocked(ProcessRecord proc) {
+        proc.whitelistManager = false;
+        for (int i=proc.services.size()-1; i>=0; i--) {
+            ServiceRecord sr = proc.services.valueAt(i);
+            if (sr.whitelistManager) {
+                proc.whitelistManager = true;
+                break;
+            }
+        }
+    }
+
     public void updateServiceConnectionActivitiesLocked(ProcessRecord clientProc) {
         ArraySet<ProcessRecord> updatedProcesses = null;
         for (int i = 0; i < clientProc.connections.size(); i++) {
@@ -997,6 +1007,9 @@
             if ((c.flags&Context.BIND_ABOVE_CLIENT) != 0) {
                 b.client.hasAboveClient = true;
             }
+            if ((c.flags&Context.BIND_ALLOW_WHITELIST_MANAGEMENT) != 0) {
+                s.whitelistManager = true;
+            }
             if (s.app != null) {
                 updateServiceClientActivitiesLocked(s.app, c, true);
             }
@@ -1019,6 +1032,9 @@
                 if ((flags&Context.BIND_TREAT_LIKE_ACTIVITY) != 0) {
                     s.app.treatLikeActivity = true;
                 }
+                if (s.whitelistManager) {
+                    s.app.whitelistManager = true;
+                }
                 // This could have made the service more important.
                 mAm.updateLruProcessLocked(s.app, s.app.hasClientActivities
                         || s.app.treatLikeActivity, b.client);
@@ -1807,6 +1823,10 @@
             }
         }
 
+        if (r.whitelistManager) {
+            app.whitelistManager = true;
+        }
+
         requestServiceBindingsLocked(r, execInFg);
 
         updateServiceClientActivitiesLocked(app, null, true);
@@ -2018,6 +2038,9 @@
                 r.stats.stopLaunchedLocked();
             }
             r.app.services.remove(r);
+            if (r.whitelistManager) {
+                updateWhitelistManagerLocked(r.app);
+            }
             if (r.app.thread != null) {
                 updateServiceForegroundLocked(r.app, false);
                 try {
@@ -2085,6 +2108,14 @@
             if ((c.flags&Context.BIND_ABOVE_CLIENT) != 0) {
                 b.client.updateHasAboveClientLocked();
             }
+            // If this connection requested whitelist management, see if we should
+            // now clear that state.
+            if ((c.flags&Context.BIND_ALLOW_WHITELIST_MANAGEMENT) != 0) {
+                s.updateWhitelistManager();
+                if (!s.whitelistManager && s.app != null) {
+                    updateWhitelistManagerLocked(s.app);
+                }
+            }
             if (s.app != null) {
                 updateServiceClientActivitiesLocked(s.app, c, true);
             }
@@ -2284,6 +2315,9 @@
             if (finishing) {
                 if (r.app != null && !r.app.persistent) {
                     r.app.services.remove(r);
+                    if (r.whitelistManager) {
+                        updateWhitelistManagerLocked(r.app);
+                    }
                 }
                 r.app = null;
             }
@@ -2379,6 +2413,9 @@
                     service.app.removed = killProcess;
                     if (!service.app.persistent) {
                         service.app.services.remove(service);
+                        if (service.whitelistManager) {
+                            updateWhitelistManagerLocked(service.app);
+                        }
                     }
                 }
                 service.app = null;
@@ -2497,6 +2534,8 @@
         updateServiceConnectionActivitiesLocked(app);
         app.connections.clear();
 
+        app.whitelistManager = false;
+
         // Clear app state from services.
         for (int i = app.services.size() - 1; i >= 0; i--) {
             ServiceRecord sr = app.services.valueAt(i);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 02d6a42d..5893f90 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -19294,7 +19294,6 @@
         }
 
         boolean mayBeTop = false;
-        app.whitelistManager = false;
 
         for (int is = app.services.size()-1;
                 is >= 0 && (adj > ProcessList.FOREGROUND_APP_ADJ
@@ -19353,9 +19352,6 @@
                         // Binding to ourself is not interesting.
                         continue;
                     }
-                    if ((cr.flags & Context.BIND_ALLOW_WHITELIST_MANAGEMENT) != 0) {
-                        app.whitelistManager = true;
-                    }
 
                     if ((cr.flags&Context.BIND_WAIVE_PRIORITY) == 0) {
                         ProcessRecord client = cr.binding.client;
diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index 0a081e9..2bfc402 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -90,6 +90,7 @@
     ProcessRecord isolatedProc; // keep track of isolated process, if requested
     ServiceState tracker; // tracking service execution, may be null
     ServiceState restartTracker; // tracking service restart
+    boolean whitelistManager; // any bindings to this service have BIND_ALLOW_WHITELIST_MANAGEMENT?
     boolean delayed;        // are we waiting to start this service in the background?
     boolean isForeground;   // is service currently in foreground mode?
     int foregroundId;       // Notification ID of last foreground req.
@@ -225,6 +226,9 @@
         if (isolatedProc != null) {
             pw.print(prefix); pw.print("isolatedProc="); pw.println(isolatedProc);
         }
+        if (whitelistManager) {
+            pw.print(prefix); pw.print("whitelistManager="); pw.println(whitelistManager);
+        }
         if (delayed) {
             pw.print(prefix); pw.print("delayed="); pw.println(delayed);
         }
@@ -391,6 +395,19 @@
         return false;
     }
 
+    public void updateWhitelistManager() {
+        whitelistManager = false;
+        for (int conni=connections.size()-1; conni>=0; conni--) {
+            ArrayList<ConnectionRecord> cr = connections.valueAt(conni);
+            for (int i=0; i<cr.size(); i++) {
+                if ((cr.get(i).flags&Context.BIND_ALLOW_WHITELIST_MANAGEMENT) != 0) {
+                    whitelistManager = true;
+                    return;
+                }
+            }
+        }
+    }
+
     public void resetRestartCounter() {
         restartCount = 0;
         restartDelay = 0;
diff --git a/services/core/java/com/android/server/content/SyncOperation.java b/services/core/java/com/android/server/content/SyncOperation.java
index 804be4e..c371f97 100644
--- a/services/core/java/com/android/server/content/SyncOperation.java
+++ b/services/core/java/com/android/server/content/SyncOperation.java
@@ -197,7 +197,7 @@
             } else if (value instanceof Boolean) {
                 syncExtrasBundle.putBoolean(key, (Boolean) value);
             } else if (value instanceof Float) {
-                syncExtrasBundle.putDouble(key, (Double) value);
+                syncExtrasBundle.putDouble(key, (double) (float) value);
             } else if (value instanceof Double) {
                 syncExtrasBundle.putDouble(key, (Double) value);
             } else if (value instanceof String) {
diff --git a/services/core/java/com/android/server/job/controllers/ContentObserverController.java b/services/core/java/com/android/server/job/controllers/ContentObserverController.java
index 9dce070..a42d0cd 100644
--- a/services/core/java/com/android/server/job/controllers/ContentObserverController.java
+++ b/services/core/java/com/android/server/job/controllers/ContentObserverController.java
@@ -22,6 +22,7 @@
 import android.net.Uri;
 import android.os.Handler;
 import android.os.UserHandle;
+import android.util.Slog;
 import android.util.TimeUtils;
 import android.util.ArrayMap;
 import android.util.ArraySet;
@@ -40,6 +41,7 @@
  */
 public class ContentObserverController extends StateController {
     private static final String TAG = "JobScheduler.Content";
+    private static final boolean DEBUG = false;
 
     /**
      * Maximum number of changing URIs we will batch together to report.
@@ -88,6 +90,9 @@
             if (taskStatus.contentObserverJobInstance == null) {
                 taskStatus.contentObserverJobInstance = new JobInstance(taskStatus);
             }
+            if (DEBUG) {
+                Slog.i(TAG, "Tracking content-trigger job " + taskStatus);
+            }
             mTrackedTasks.add(taskStatus);
             boolean havePendingUris = false;
             // If there is a previous job associated with the new job, propagate over
@@ -175,6 +180,9 @@
                     taskStatus.contentObserverJobInstance = null;
                 }
             }
+            if (DEBUG) {
+                Slog.i(TAG, "No longer tracking job " + taskStatus);
+            }
             mTrackedTasks.remove(taskStatus);
         }
     }
@@ -194,16 +202,20 @@
     }
 
     final class ObserverInstance extends ContentObserver {
-        final Uri mUri;
+        final JobInfo.TriggerContentUri mUri;
         final ArraySet<JobInstance> mJobs = new ArraySet<>();
 
-        public ObserverInstance(Handler handler, Uri uri) {
+        public ObserverInstance(Handler handler, JobInfo.TriggerContentUri uri) {
             super(handler);
             mUri = uri;
         }
 
         @Override
         public void onChange(boolean selfChange, Uri uri) {
+            if (DEBUG) {
+                Slog.i(TAG, "onChange(self=" + selfChange + ") for " + uri
+                        + " when mUri=" + mUri);
+            }
             synchronized (mLock) {
                 final int N = mJobs.size();
                 for (int i=0; i<N; i++) {
@@ -255,14 +267,25 @@
                 for (JobInfo.TriggerContentUri uri : uris) {
                     ObserverInstance obs = mObservers.get(uri);
                     if (obs == null) {
-                        obs = new ObserverInstance(mHandler, uri.getUri());
+                        obs = new ObserverInstance(mHandler, uri);
                         mObservers.put(uri, obs);
+                        final boolean andDescendants = (uri.getFlags() &
+                                JobInfo.TriggerContentUri.FLAG_NOTIFY_FOR_DESCENDANTS) != 0;
+                        if (DEBUG) {
+                            Slog.v(TAG, "New observer " + obs + " for " + uri.getUri()
+                                    + " andDescendants=" + andDescendants);
+                        }
                         mContext.getContentResolver().registerContentObserver(
                                 uri.getUri(),
-                                (uri.getFlags() &
-                                        JobInfo.TriggerContentUri.FLAG_NOTIFY_FOR_DESCENDANTS)
-                                    != 0,
+                                andDescendants,
                                 obs);
+                    } else {
+                        if (DEBUG) {
+                            final boolean andDescendants = (uri.getFlags() &
+                                    JobInfo.TriggerContentUri.FLAG_NOTIFY_FOR_DESCENDANTS) != 0;
+                            Slog.v(TAG, "Reusing existing observer " + obs + " for " + uri.getUri()
+                                    + " andDescendants=" + andDescendants);
+                        }
                     }
                     obs.mJobs.add(this);
                     mMyObservers.add(obs);
@@ -315,8 +338,11 @@
                 final ObserverInstance obs = mMyObservers.get(i);
                 obs.mJobs.remove(this);
                 if (obs.mJobs.size() == 0) {
+                    if (DEBUG) {
+                        Slog.i(TAG, "Unregistering observer " + obs + " for " + obs.mUri.getUri());
+                    }
                     mContext.getContentResolver().unregisterContentObserver(obs);
-                    mObservers.remove(obs);
+                    mObservers.remove(obs.mUri);
                 }
             }
         }
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 59de263..9209d3d 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -2598,7 +2598,6 @@
         final long duration = LocalServices.getService(DeviceIdleController.LocalService.class)
                 .getNotificationWhitelistDuration();
 
-        int size = 0;
         if (notification.contentIntent != null) {
             am.setPendingIntentWhitelistDuration(notification.contentIntent.getTarget(), duration);
         }
@@ -2615,45 +2614,19 @@
                     continue;
                 }
                 am.setPendingIntentWhitelistDuration(action.actionIntent.getTarget(), duration);
-                setPendingIntentWhitelistDuration(am, duration, action.getExtras());
-                final RemoteInput[] remoteInputs = action.getRemoteInputs();
-                if (remoteInputs != null) {
-                    for (RemoteInput remoteInput : remoteInputs) {
-                        setPendingIntentWhitelistDuration(am, duration, remoteInput.getExtras());
-                    }
-                }
             }
         }
-    }
-
-    private static void setPendingIntentWhitelistDuration(ActivityManagerInternal am, long duration,
-            Bundle extras) {
-        for (String key : extras.keySet()) {
-            final Object value = extras.get(key);
-            if (value instanceof Parcelable) {
-                setPendingIntentWhitelistDuration(am, duration, (Parcelable) value);
-            } else if (value instanceof Parcelable[]) {
-                for (Parcelable parcelable : (Parcelable[]) value) {
-                    setPendingIntentWhitelistDuration(am, duration, parcelable);
-                }
-            } else if (value instanceof List) {
-                for (Object element : (List <?>) value) {
-                    if (element instanceof Parcelable) {
-                        setPendingIntentWhitelistDuration(am, duration, (Parcelable) element);
-                    }
+        if (notification.extrasPendingIntents != null) {
+            final int intentCount = notification.extrasPendingIntents.size();
+            for (int i = 0; i < intentCount; i++) {
+                PendingIntent pendingIntent = notification.extrasPendingIntents.valueAt(i);
+                if (pendingIntent != null) {
+                    am.setPendingIntentWhitelistDuration(pendingIntent.getTarget(), duration);
                 }
             }
         }
     }
 
-    private static void setPendingIntentWhitelistDuration(ActivityManagerInternal am, long duration,
-            Parcelable parcelable) {
-        if (parcelable instanceof PendingIntent) {
-            am.setPendingIntentWhitelistDuration(((PendingIntent) parcelable).getTarget(),
-                    duration);
-        }
-    }
-
     private class EnqueueNotificationRunnable implements Runnable {
         private final NotificationRecord r;
         private final int userId;
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index 7b85a4f..72c549f 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -61,6 +61,13 @@
         mInstaller = new InstallerConnection();
     }
 
+    // Package-private installer that accepts a custom InstallerConnection. Used for
+    // OtaDexoptService.
+    Installer(Context context, InstallerConnection connection) {
+        super(context);
+        mInstaller = connection;
+    }
+
     /**
      * Yell loudly if someone tries making future calls while holding a lock on
      * the given object.
diff --git a/services/core/java/com/android/server/pm/OtaDexoptService.java b/services/core/java/com/android/server/pm/OtaDexoptService.java
index df91f4a1..01b3dc2 100644
--- a/services/core/java/com/android/server/pm/OtaDexoptService.java
+++ b/services/core/java/com/android/server/pm/OtaDexoptService.java
@@ -32,10 +32,12 @@
 import android.util.Log;
 import android.util.Slog;
 
+import com.android.internal.os.InstallerConnection;
 import com.android.internal.os.InstallerConnection.InstallerException;
 
 import java.io.File;
 import java.io.FileDescriptor;
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
 
@@ -49,21 +51,28 @@
     private final static boolean DEBUG_DEXOPT = true;
 
     private final Context mContext;
-    private final PackageDexOptimizer mPackageDexOptimizer;
     private final PackageManagerService mPackageManagerService;
 
     // TODO: Evaluate the need for WeakReferences here.
+
+    /**
+     * The list of packages to dexopt.
+     */
     private List<PackageParser.Package> mDexoptPackages;
+
+    /**
+     * The list of dexopt invocations for the current package (which will no longer be in
+     * mDexoptPackages). This can be more than one as a package may have multiple code paths,
+     * e.g., in the split-APK case.
+     */
+    private List<String> mCommandsForCurrentPackage;
+
     private int completeSize;
 
     public OtaDexoptService(Context context, PackageManagerService packageManagerService) {
         this.mContext = context;
         this.mPackageManagerService = packageManagerService;
 
-        // Use the package manager install and install lock here for the OTA dex optimizer.
-        mPackageDexOptimizer = new OTADexoptPackageDexOptimizer(packageManagerService.mInstaller,
-                packageManagerService.mInstallLock, context);
-
         // Now it's time to check whether we need to move any A/B artifacts.
         moveAbArtifacts(packageManagerService.mInstaller);
     }
@@ -93,6 +102,7 @@
                     mPackageManagerService.mPackages.values(), mPackageManagerService);
         }
         completeSize = mDexoptPackages.size();
+        mCommandsForCurrentPackage = null;
     }
 
     @Override
@@ -101,6 +111,7 @@
             Log.i(TAG, "Cleaning up OTA Dexopt state.");
         }
         mDexoptPackages = null;
+        mCommandsForCurrentPackage = null;
     }
 
     @Override
@@ -109,15 +120,109 @@
             throw new IllegalStateException("done() called before prepare()");
         }
 
-        return mDexoptPackages.isEmpty();
+        return mDexoptPackages.isEmpty() && (mCommandsForCurrentPackage == null);
     }
 
     @Override
     public synchronized float getProgress() throws RemoteException {
+        // We approximate by number of packages here. We could track all compiles, if we
+        // generated them ahead of time. Right now we're trying to conserve memory.
         if (completeSize == 0) {
             return 1f;
         }
-        return (completeSize - mDexoptPackages.size()) / ((float)completeSize);
+        int packagesLeft = mDexoptPackages.size() + (mCommandsForCurrentPackage != null ? 1 : 0);
+        return (completeSize - packagesLeft) / ((float)completeSize);
+    }
+
+    /**
+     * Return the next dexopt command for the current package. Enforces the invariant
+     */
+    private String getNextPackageDexopt() {
+        if (mCommandsForCurrentPackage != null) {
+            String next = mCommandsForCurrentPackage.remove(0);
+            if (mCommandsForCurrentPackage.isEmpty()) {
+                mCommandsForCurrentPackage = null;
+            }
+            return next;
+        }
+        return null;
+    }
+
+    @Override
+    public synchronized String nextDexoptCommand() throws RemoteException {
+        if (mDexoptPackages == null) {
+            throw new IllegalStateException("dexoptNextPackage() called before prepare()");
+        }
+
+        // Get the next command.
+        for (;;) {
+            // Check whether there's one for the current package.
+            String next = getNextPackageDexopt();
+            if (next != null) {
+                return next;
+            }
+
+            // Move to the next package, if possible.
+            if (mDexoptPackages.isEmpty()) {
+                return "Nothing to do";
+            }
+
+            PackageParser.Package nextPackage = mDexoptPackages.remove(0);
+
+            if (DEBUG_DEXOPT) {
+                Log.i(TAG, "Processing " + nextPackage.packageName + " for OTA dexopt.");
+            }
+
+            // Generate the next mPackageDexopts state. Ignore errors, this loop is strongly
+            // monotonically increasing, anyways.
+            generatePackageDexopts(nextPackage);
+
+            // Invariant check: mPackageDexopts is null or not empty.
+            if (mCommandsForCurrentPackage != null && mCommandsForCurrentPackage.isEmpty()) {
+                cleanup();
+                throw new IllegalStateException("mPackageDexopts empty for " + nextPackage);
+            }
+        }
+    }
+
+    /**
+     * Generate all dexopt commands for the given package and place them into mPackageDexopts.
+     * Returns true on success, false in an error situation like low disk space.
+     */
+    private synchronized boolean generatePackageDexopts(PackageParser.Package nextPackage) {
+        // Check for low space.
+        // TODO: If apps are not installed in the internal /data partition, we should compare
+        //       against that storage's free capacity.
+        File dataDir = Environment.getDataDirectory();
+        @SuppressWarnings("deprecation")
+        long lowThreshold = StorageManager.from(mContext).getStorageLowBytes(dataDir);
+        if (lowThreshold == 0) {
+            throw new IllegalStateException("Invalid low memory threshold");
+        }
+        long usableSpace = dataDir.getUsableSpace();
+        if (usableSpace < lowThreshold) {
+            Log.w(TAG, "Not running dexopt on " + nextPackage.packageName + " due to low memory: " +
+                    usableSpace);
+            return false;
+        }
+
+        // Use our custom connection that just collects the commands.
+        RecordingInstallerConnection collectingConnection = new RecordingInstallerConnection();
+        Installer collectingInstaller = new Installer(mContext, collectingConnection);
+
+        // Use the package manager install and install lock here for the OTA dex optimizer.
+        PackageDexOptimizer optimizer = new OTADexoptPackageDexOptimizer(
+                collectingInstaller, mPackageManagerService.mInstallLock, mContext);
+        optimizer.performDexOpt(nextPackage, nextPackage.usesLibraryFiles,
+                null /* ISAs */, false /* checkProfiles */,
+                getCompilerFilterForReason(PackageManagerService.REASON_AB_OTA));
+
+        mCommandsForCurrentPackage = collectingConnection.commands;
+        if (mCommandsForCurrentPackage.isEmpty()) {
+            mCommandsForCurrentPackage = null;
+        }
+
+        return true;
     }
 
     @Override
@@ -152,8 +257,10 @@
             return;
         }
 
-        mPackageDexOptimizer.performDexOpt(nextPackage, nextPackage.usesLibraryFiles,
-                null /* ISAs */, false /* checkProfiles */,
+        PackageDexOptimizer optimizer = new OTADexoptPackageDexOptimizer(
+                mPackageManagerService.mInstaller, mPackageManagerService.mInstallLock, mContext);
+        optimizer.performDexOpt(nextPackage, nextPackage.usesLibraryFiles, null /* ISAs */,
+                false /* checkProfiles */,
                 getCompilerFilterForReason(PackageManagerService.REASON_AB_OTA));
     }
 
@@ -218,4 +325,40 @@
         }
 
     }
+
+    private static class RecordingInstallerConnection extends InstallerConnection {
+        public List<String> commands = new ArrayList<String>(1);
+
+        @Override
+        public void setWarnIfHeld(Object warnIfHeld) {
+            throw new IllegalStateException("Should not reach here");
+        }
+
+        @Override
+        public synchronized String transact(String cmd) {
+            commands.add(cmd);
+            return "0";
+        }
+
+        @Override
+        public boolean mergeProfiles(int uid, String pkgName) throws InstallerException {
+            throw new IllegalStateException("Should not reach here");
+        }
+
+        @Override
+        public boolean dumpProfiles(String gid, String packageName, String codePaths)
+                throws InstallerException {
+            throw new IllegalStateException("Should not reach here");
+        }
+
+        @Override
+        public void disconnect() {
+            throw new IllegalStateException("Should not reach here");
+        }
+
+        @Override
+        public void waitForConnection() {
+            throw new IllegalStateException("Should not reach here");
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/pm/OtaDexoptShellCommand.java b/services/core/java/com/android/server/pm/OtaDexoptShellCommand.java
index e8fdfa5..bbd4048 100644
--- a/services/core/java/com/android/server/pm/OtaDexoptShellCommand.java
+++ b/services/core/java/com/android/server/pm/OtaDexoptShellCommand.java
@@ -46,6 +46,8 @@
                     return runOtaDone();
                 case "step":
                     return runOtaStep();
+                case "next":
+                    return runOtaNext();
                 case "progress":
                     return runOtaProgress();
                 default:
@@ -83,6 +85,11 @@
         return 0;
     }
 
+    private int runOtaNext() throws RemoteException {
+        getOutPrintWriter().println(mInterface.nextDexoptCommand());
+        return 0;
+    }
+
     private int runOtaProgress() throws RemoteException {
         final float progress = mInterface.getProgress();
         final PrintWriter pw = getOutPrintWriter();
@@ -103,6 +110,8 @@
         pw.println("    Replies whether the OTA is complete or not.");
         pw.println("  step");
         pw.println("    OTA dexopt the next package.");
+        pw.println("  next");
+        pw.println("    Get parameters for OTA dexopt of the next package.");
         pw.println("  cleanup");
         pw.println("    Clean up internal states. Ends an OTA session.");
     }
diff --git a/services/retaildemo/java/com/android/server/retaildemo/RetailDemoModeService.java b/services/retaildemo/java/com/android/server/retaildemo/RetailDemoModeService.java
index 96e6da74..51b1e38 100644
--- a/services/retaildemo/java/com/android/server/retaildemo/RetailDemoModeService.java
+++ b/services/retaildemo/java/com/android/server/retaildemo/RetailDemoModeService.java
@@ -55,6 +55,8 @@
 import android.util.Slog;
 import com.android.internal.os.BackgroundThread;
 import com.android.internal.R;
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.logging.MetricsLogger;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.server.LocalServices;
 import com.android.server.ServiceThread;
@@ -86,8 +88,12 @@
             AudioSystem.STREAM_MUSIC
     };
 
+    // Tron Vars
+    private static final String DEMO_SESSION_COUNT = "retail_demo_session_count";
+    private static final String DEMO_SESSION_DURATION = "retail_demo_session_duration";
+
     boolean mDeviceInDemoMode = false;
-    int mCurrentUserId;
+    int mCurrentUserId = UserHandle.USER_SYSTEM;
     private ActivityManagerService mAms;
     private ActivityManagerInternal mAmi;
     private AudioManager mAudioManager;
@@ -102,6 +108,15 @@
     private String[] mCameraIdsWithFlash;
     private Configuration mPrimaryUserConfiguration;
 
+    final Object mActivityLock = new Object();
+    // Whether the newly created demo user has interacted with the screen yet
+    @GuardedBy("mActivityLock")
+    boolean mUserUntouched;
+    @GuardedBy("mActivityLock")
+    long mFirstUserActivityTime;
+    @GuardedBy("mActivityLock")
+    long mLastUserActivityTime;
+
     private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
@@ -136,18 +151,7 @@
                     mWakeLock.acquire();
                     break;
                 case MSG_INACTIVITY_TIME_OUT:
-                    final IPackageManager pm = AppGlobals.getPackageManager();
-                    int enabledState = PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
-                    String demoLauncherComponent = getContext().getResources()
-                            .getString(R.string.config_demoModeLauncherComponent);
-                    try {
-                        enabledState = pm.getComponentEnabledSetting(
-                                ComponentName.unflattenFromString(demoLauncherComponent),
-                                mCurrentUserId);
-                    } catch (RemoteException exc) {
-                        Slog.e(TAG, "Unable to talk to Package Manager", exc);
-                    }
-                    if (enabledState == PackageManager.COMPONENT_ENABLED_STATE_DISABLED) {
+                    if (isDemoLauncherDisabled()) {
                         Slog.i(TAG, "User inactivity timeout reached");
                         showInactivityCountdownDialog();
                     }
@@ -158,6 +162,9 @@
                     }
                     removeMessages(MSG_START_NEW_SESSION);
                     removeMessages(MSG_INACTIVITY_TIME_OUT);
+                    if (mCurrentUserId != UserHandle.USER_SYSTEM) {
+                        logSessionDuration();
+                    }
                     final UserInfo demoUser = getUserManager().createUser(DEMO_USER_NAME,
                             UserInfo.FLAG_DEMO | UserInfo.FLAG_EPHEMERAL);
                     if (demoUser != null) {
@@ -190,6 +197,9 @@
 
     public RetailDemoModeService(Context context) {
         super(context);
+        synchronized (mActivityLock) {
+            mFirstUserActivityTime = mLastUserActivityTime = SystemClock.uptimeMillis();
+        }
     }
 
     private Notification createResetNotification() {
@@ -213,6 +223,21 @@
         return mResetDemoPendingIntent;
     }
 
+    boolean isDemoLauncherDisabled() {
+        IPackageManager pm = AppGlobals.getPackageManager();
+        int enabledState = PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
+        String demoLauncherComponent = getContext().getResources()
+                .getString(R.string.config_demoModeLauncherComponent);
+        try {
+            enabledState = pm.getComponentEnabledSetting(
+                    ComponentName.unflattenFromString(demoLauncherComponent),
+                    mCurrentUserId);
+        } catch (RemoteException exc) {
+            Slog.e(TAG, "Unable to talk to Package Manager", exc);
+        }
+        return enabledState == PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
+    }
+
     private void setupDemoUser(UserInfo userInfo) {
         UserManager um = getUserManager();
         UserHandle user = UserHandle.of(userInfo.id);
@@ -226,6 +251,14 @@
                 Settings.Secure.SKIP_FIRST_USE_HINTS, 1, userInfo.id);
     }
 
+    void logSessionDuration() {
+        final int sessionDuration;
+        synchronized (mActivityLock) {
+            sessionDuration = (int) ((mLastUserActivityTime - mFirstUserActivityTime) / 1000);
+        }
+        MetricsLogger.count(getContext(), DEMO_SESSION_DURATION, sessionDuration);
+    }
+
     private ActivityManagerService getActivityManager() {
         if (mAms == null) {
             mAms = (ActivityManagerService) ActivityManagerNative.getDefault();
@@ -395,11 +428,15 @@
         turnOffAllFlashLights();
         muteVolumeStreams();
         mAmi.updatePersistentConfigurationForUser(getPrimaryUsersConfiguration(), userId);
+        synchronized (mActivityLock) {
+            mUserUntouched = true;
+        }
+        MetricsLogger.count(getContext(), DEMO_SESSION_COUNT, 1);
+        mHandler.removeMessages(MSG_INACTIVITY_TIME_OUT);
     }
 
     private RetailDemoModeServiceInternal mLocalService = new RetailDemoModeServiceInternal() {
         private static final long USER_ACTIVITY_DEBOUNCE_TIME = 2000;
-        private long mLastUserActivityTime = 0;
 
         @Override
         public void onUserActivity() {
@@ -407,10 +444,17 @@
                 return;
             }
             long timeOfActivity = SystemClock.uptimeMillis();
-            if (timeOfActivity < mLastUserActivityTime + USER_ACTIVITY_DEBOUNCE_TIME) {
-                return;
+            synchronized (mActivityLock) {
+                if (timeOfActivity < mLastUserActivityTime + USER_ACTIVITY_DEBOUNCE_TIME) {
+                    return;
+                }
+                mLastUserActivityTime = timeOfActivity;
+                if (mUserUntouched && isDemoLauncherDisabled()) {
+                    Slog.d(TAG, "retail_demo first touch");
+                    mUserUntouched = false;
+                    mFirstUserActivityTime = timeOfActivity;
+                }
             }
-            mLastUserActivityTime = timeOfActivity;
             mHandler.removeMessages(MSG_INACTIVITY_TIME_OUT);
             mHandler.sendEmptyMessageDelayed(MSG_INACTIVITY_TIME_OUT, USER_INACTIVITY_TIMEOUT);
         }
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index 3930208..1b14d65 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -375,6 +375,14 @@
             "android.telecom.extra.ANSWERING_DROPS_FG_CALL";
 
     /**
+     * Boolean connection extra key on a {@link Connection} which indicates that adding an
+     * additional call is disallowed when there is a video call in progress.
+     * @hide
+     */
+    public static final String EXTRA_DISABLE_ADD_CALL_DURING_VIDEO_CALL =
+            "android.telecom.extra.DISABLE_ADD_CALL_DURING_VIDEO_CALL";
+
+    /**
      * Connection event used to inform Telecom that it should play the on hold tone.  This is used
      * to play a tone when the peer puts the current call on hold.  Sent to Telecom via
      * {@link #sendConnectionEvent(String)}.
@@ -586,6 +594,8 @@
         public void onExtrasChanged(Connection c, Bundle extras) {}
         public void onExtrasRemoved(Connection c, List<String> keys) {}
         public void onConnectionEvent(Connection c, String event, Bundle extras) {}
+        /** @hide */
+        public void onConferenceSupportedChanged(Connection c, boolean isConferenceSupported) {}
     }
 
     /**
@@ -2355,6 +2365,19 @@
     }
 
     /**
+     * Notifies listeners when a change has occurred to the Connection which impacts its ability to
+     * be a part of a conference call.
+     * @param isConferenceSupported {@code true} if the connection supports being part of a
+     *      conference call, {@code false} otherwise.
+     * @hide
+     */
+    protected void notifyConferenceSupportedChanged(boolean isConferenceSupported) {
+        for (Listener l : mListeners) {
+            l.onConferenceSupportedChanged(this, isConferenceSupported);
+        }
+    }
+
+    /**
      * Sends an event associated with this {@code Connection}, with associated event extras.
      *
      * Events are exposed to {@link InCallService} implementations via the
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 2458cc8..a5e50aa 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -808,6 +808,22 @@
     public static final String KEY_DROP_VIDEO_CALL_WHEN_ANSWERING_AUDIO_CALL_BOOL =
             "drop_video_call_when_answering_audio_call_bool";
 
+    /**
+     * Flag indicating whether the carrier supports merging wifi calls when VoWIFI is disabled.
+     * This can happen in the case of a carrier which allows offloading video calls to WIFI
+     * separately of whether voice over wifi is enabled.  In such a scenario when two video calls
+     * are downgraded to voice, they remain over wifi.  However, if VoWIFI is disabled, these calls
+     * cannot be merged.
+     */
+    public static final String KEY_ALLOW_MERGE_WIFI_CALLS_WHEN_VOWIFI_OFF_BOOL =
+            "allow_merge_wifi_calls_when_vowifi_off_bool";
+
+    /**
+     * When true, indicates that adding a call is disabled when there is an ongoing video call.
+     */
+    public static final String KEY_ALLOW_ADD_CALL_DURING_VIDEO_CALL_BOOL =
+            "allow_add_call_during_video_call";
+
     /** The default value for every variable. */
     private final static PersistableBundle sDefaults;
 
@@ -959,8 +975,10 @@
         // Order is important - lowest precidence first
         sDefaults.putStringArray(KEY_RATCHET_RAT_FAMILIES,
                 new String[]{"1,2","7,8,12","3,11,9,10,15","14,19"});
-        sDefaults.putBoolean(KEY_TREAT_DOWNGRADED_VIDEO_CALLS_AS_VIDEO_CALLS_BOOL, true);
-        sDefaults.putBoolean(KEY_DROP_VIDEO_CALL_WHEN_ANSWERING_AUDIO_CALL_BOOL, true);
+        sDefaults.putBoolean(KEY_TREAT_DOWNGRADED_VIDEO_CALLS_AS_VIDEO_CALLS_BOOL, false);
+        sDefaults.putBoolean(KEY_DROP_VIDEO_CALL_WHEN_ANSWERING_AUDIO_CALL_BOOL, false);
+        sDefaults.putBoolean(KEY_ALLOW_MERGE_WIFI_CALLS_WHEN_VOWIFI_OFF_BOOL, true);
+        sDefaults.putBoolean(KEY_ALLOW_ADD_CALL_DURING_VIDEO_CALL_BOOL, true);
     }
 
     /**