Merge "Frameworks/base: Refactor Paint & Canvas native allocation support" into nyc-dev
diff --git a/api/current.txt b/api/current.txt
index f7a316d..d4f9d73 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -36419,6 +36419,7 @@
   public class TelecomManager {
     method public void addNewIncomingCall(android.telecom.PhoneAccountHandle, android.os.Bundle);
     method public void cancelMissedCallsNotification();
+    method public android.content.Intent createManageBlockedNumbersIntent();
     method public android.net.Uri getAdnUriForPhoneAccount(android.telecom.PhoneAccountHandle);
     method public java.util.List<android.telecom.PhoneAccountHandle> getCallCapablePhoneAccounts();
     method public java.lang.String getDefaultDialerPackage();
diff --git a/api/system-current.txt b/api/system-current.txt
index b2b5982..db90b89 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -39118,6 +39118,7 @@
     method public void cancelMissedCallsNotification();
     method public deprecated void clearAccounts();
     method public void clearPhoneAccounts();
+    method public android.content.Intent createManageBlockedNumbersIntent();
     method public java.util.List<android.telecom.ParcelableCallAnalytics> dumpAnalytics();
     method public void enablePhoneAccount(android.telecom.PhoneAccountHandle, boolean);
     method public boolean endCall();
diff --git a/api/test-current.txt b/api/test-current.txt
index 472daf6..ad3a5e6 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -36434,6 +36434,7 @@
   public class TelecomManager {
     method public void addNewIncomingCall(android.telecom.PhoneAccountHandle, android.os.Bundle);
     method public void cancelMissedCallsNotification();
+    method public android.content.Intent createManageBlockedNumbersIntent();
     method public android.net.Uri getAdnUriForPhoneAccount(android.telecom.PhoneAccountHandle);
     method public java.util.List<android.telecom.PhoneAccountHandle> getCallCapablePhoneAccounts();
     method public java.lang.String getDefaultDialerPackage();
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 0020a50..0d852e5 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -75,6 +75,7 @@
 import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.os.Trace;
+import android.os.TransactionTooLargeException;
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.security.NetworkSecurityPolicy;
@@ -3677,6 +3678,12 @@
                 ActivityManagerNative.getDefault().activityStopped(
                     activity.token, state, persistentState, description);
             } catch (RemoteException ex) {
+                if (ex instanceof TransactionTooLargeException
+                        && "com.google.android.gms".equals(activity.packageInfo.getPackageName())) {
+                    Log.d(TAG, "STAHP SENDING SO MUCH DATA KTHX: " + ex);
+                    return;
+                }
+
                 throw ex.rethrowFromSystemServer();
             }
         }
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index ae433eb..06fe515 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -225,6 +225,10 @@
         return mApplicationInfo;
     }
 
+    public int getTargetSdkVersion() {
+        return mApplicationInfo.targetSdkVersion;
+    }
+
     public boolean isSecurityViolation() {
         return mSecurityViolation;
     }
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index a9150e8..a54f40f 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -68,6 +68,7 @@
 
 import java.io.IOException;
 import java.io.InputStream;
+import java.util.Arrays;
 import java.util.Locale;
 
 /**
@@ -119,6 +120,9 @@
     private static final LongSparseArray<android.content.res.ConstantState<ComplexColor>>
             sPreloadedComplexColors = new LongSparseArray<>();
 
+    /** Size of the cyclical cache used to map XML files to blocks. */
+    private static final int XML_BLOCK_CACHE_SIZE = 4;
+
     // Pool of TypedArrays targeted to this Resources object.
     final SynchronizedPool<TypedArray> mTypedArrayPool = new SynchronizedPool<>(5);
 
@@ -154,8 +158,9 @@
 
     // Cyclical cache used for recently-accessed XML files.
     private int mLastCachedXmlBlockIndex = -1;
-    private final String[] mCachedXmlBlockFiles = new String[4];
-    private final XmlBlock[] mCachedXmlBlocks = new XmlBlock[4];
+    private final int[] mCachedXmlBlockCookies = new int[XML_BLOCK_CACHE_SIZE];
+    private final String[] mCachedXmlBlockFiles = new String[XML_BLOCK_CACHE_SIZE];
+    private final XmlBlock[] mCachedXmlBlocks = new XmlBlock[XML_BLOCK_CACHE_SIZE];
 
     final AssetManager mAssets;
     final ClassLoader mClassLoader;
@@ -2339,18 +2344,18 @@
      * tools.
      */
     public final void flushLayoutCache() {
-        final String[] cachedXmlBlockFiles = mCachedXmlBlockFiles;
-        final XmlBlock[] cachedXmlBlocks = mCachedXmlBlocks;
-        synchronized (cachedXmlBlockFiles) {
-            final int num = cachedXmlBlockFiles.length;
-            for (int i = 0; i < num; i++) {
+        synchronized (mCachedXmlBlocks) {
+            Arrays.fill(mCachedXmlBlockCookies, 0);
+            Arrays.fill(mCachedXmlBlockFiles, null);
+
+            final XmlBlock[] cachedXmlBlocks = mCachedXmlBlocks;
+            for (int i = 0; i < XML_BLOCK_CACHE_SIZE; i++) {
                 final XmlBlock oldBlock = cachedXmlBlocks[i];
                 if (oldBlock != null) {
                     oldBlock.close();
                 }
-                cachedXmlBlockFiles[i] = null;
-                cachedXmlBlocks[i] = null;
             }
+            Arrays.fill(cachedXmlBlocks, null);
         }
     }
 
@@ -2852,13 +2857,14 @@
             int assetCookie, @NonNull String type) throws NotFoundException {
         if (id != 0) {
             try {
-                final String[] cachedXmlBlockFiles = mCachedXmlBlockFiles;
-                final XmlBlock[] cachedXmlBlocks = mCachedXmlBlocks;
-                synchronized (cachedXmlBlockFiles) {
+                synchronized (mCachedXmlBlocks) {
+                    final int[] cachedXmlBlockCookies = mCachedXmlBlockCookies;
+                    final String[] cachedXmlBlockFiles = mCachedXmlBlockFiles;
+                    final XmlBlock[] cachedXmlBlocks = mCachedXmlBlocks;
                     // First see if this block is in our cache.
                     final int num = cachedXmlBlockFiles.length;
                     for (int i = 0; i < num; i++) {
-                        if (cachedXmlBlockFiles[i] != null
+                        if (cachedXmlBlockCookies[i] == assetCookie && cachedXmlBlockFiles[i] != null
                                 && cachedXmlBlockFiles[i].equals(file)) {
                             return cachedXmlBlocks[i].newParser();
                         }
@@ -2874,6 +2880,7 @@
                         if (oldBlock != null) {
                             oldBlock.close();
                         }
+                        cachedXmlBlockCookies[pos] = assetCookie;
                         cachedXmlBlockFiles[pos] = file;
                         cachedXmlBlocks[pos] = block;
                         return block.newParser();
diff --git a/core/java/android/os/HardwarePropertiesManager.java b/core/java/android/os/HardwarePropertiesManager.java
index c72a6481..f48306a 100644
--- a/core/java/android/os/HardwarePropertiesManager.java
+++ b/core/java/android/os/HardwarePropertiesManager.java
@@ -69,7 +69,6 @@
      * @return an array of requested float device temperatures.
      *         Empty if platform doesn't provide the queried temperature.
      *
-     * @throws IllegalArgumentException if an incorrect temperature type is queried.
      * @throws SecurityException if a non profile or device owner tries to call this method.
     */
     public @NonNull float[] getDeviceTemperatures(@DeviceTemperatureType int type) {
@@ -84,7 +83,8 @@
                 return new float[0];
             }
         default:
-            throw new IllegalArgumentException();
+            Log.w(TAG, "Unknown device temperature type.");
+            return new float[0];
         }
     }
 
diff --git a/core/java/android/widget/SearchView.java b/core/java/android/widget/SearchView.java
index 3796df7..9a4d69f 100644
--- a/core/java/android/widget/SearchView.java
+++ b/core/java/android/widget/SearchView.java
@@ -1358,6 +1358,17 @@
                     + Integer.toHexString(System.identityHashCode(this))
                     + " isIconified=" + isIconified + "}";
         }
+
+        public static final Parcelable.Creator<SavedState> CREATOR =
+                new Parcelable.Creator<SavedState>() {
+                    public SavedState createFromParcel(Parcel in) {
+                        return new SavedState(in);
+                    }
+
+                    public SavedState[] newArray(int size) {
+                        return new SavedState[size];
+                    }
+                };
     }
 
     @Override
diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp
index ae109c6..abc6c4b 100644
--- a/core/jni/android_util_Binder.cpp
+++ b/core/jni/android_util_Binder.cpp
@@ -556,7 +556,7 @@
     }
 
     // For the rest of the function we will hold this lock, to serialize
-    // looking/creation of Java proxies for native Binder proxies.
+    // looking/creation/destruction of Java proxies for native Binder proxies.
     AutoMutex _l(mProxyLock);
 
     // Someone else's...  do we know about it?
@@ -1225,16 +1225,21 @@
 
 static void android_os_BinderProxy_destroy(JNIEnv* env, jobject obj)
 {
+    // Don't race with construction/initialization
+    AutoMutex _l(mProxyLock);
+
     IBinder* b = (IBinder*)
             env->GetLongField(obj, gBinderProxyOffsets.mObject);
     DeathRecipientList* drl = (DeathRecipientList*)
             env->GetLongField(obj, gBinderProxyOffsets.mOrgue);
 
     LOGDEATH("Destroying BinderProxy %p: binder=%p drl=%p\n", obj, b, drl);
-    env->SetLongField(obj, gBinderProxyOffsets.mObject, 0);
-    env->SetLongField(obj, gBinderProxyOffsets.mOrgue, 0);
-    drl->decStrong((void*)javaObjectForIBinder);
-    b->decStrong((void*)javaObjectForIBinder);
+    if (b != nullptr) {
+        env->SetLongField(obj, gBinderProxyOffsets.mObject, 0);
+        env->SetLongField(obj, gBinderProxyOffsets.mOrgue, 0);
+        drl->decStrong((void*)javaObjectForIBinder);
+        b->decStrong((void*)javaObjectForIBinder);
+    }
 
     IPCThreadState::self()->flushCommands();
 }
diff --git a/packages/SystemUI/res/drawable/tv_pip_play_button.xml b/packages/SystemUI/res/drawable/tv_pip_play_button.xml
new file mode 100644
index 0000000..fecdc09
--- /dev/null
+++ b/packages/SystemUI/res/drawable/tv_pip_play_button.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:constantSize="true">
+    <item android:state_focused="true">
+        <layer-list>
+            <item android:drawable="@drawable/tv_pip_button_focused" />
+            <item android:drawable="@drawable/ic_play_arrow_white_24dp" />
+        </layer-list>
+    </item>
+    <item android:drawable="@drawable/ic_play_arrow_white_24dp" />
+</selector>
diff --git a/packages/SystemUI/res/layout/tv_pip_menu.xml b/packages/SystemUI/res/layout/tv_pip_menu.xml
index 0b98d0e..1fec49e 100644
--- a/packages/SystemUI/res/layout/tv_pip_menu.xml
+++ b/packages/SystemUI/res/layout/tv_pip_menu.xml
@@ -34,7 +34,7 @@
         android:gravity="center"
         android:clipChildren="false">
 
-        <ImageView android:id="@+id/full"
+        <ImageView android:id="@+id/full_button"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:focusable="true"
@@ -53,17 +53,16 @@
             android:clipChildren="false" />
     </LinearLayout>
 
-    <LinearLayout
+    <LinearLayout android:id="@+id/play_pause"
         android:layout_width="34dp"
         android:layout_height="wrap_content"
         android:layout_marginStart="3dp"
         android:layout_marginEnd="3dp"
         android:orientation="vertical"
         android:gravity="center"
-        android:visibility="gone"
         android:clipChildren="false">
 
-        <ImageView android:id="@+id/play_pause"
+        <ImageView android:id="@+id/play_pause_button"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:focusable="true"
@@ -90,7 +89,7 @@
         android:gravity="center"
         android:clipChildren="false">
 
-        <ImageView android:id="@+id/close"
+        <ImageView android:id="@+id/close_button"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:focusable="true"
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
index c31bb33..c643d67 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
@@ -54,8 +54,7 @@
         mQsContainer = container;
         mQuickQsPanel = quickPanel;
         mQsPanel = panel;
-        mQuickQsPanel.addOnLayoutChangeListener(this);
-        mQsPanel.addOnLayoutChangeListener(this);
+        container.addOnLayoutChangeListener(this);
         QSTileLayout tileLayout = mQsPanel.getTileLayout();
         if (tileLayout instanceof PagedTileLayout) {
             ((PagedTileLayout) tileLayout).setPageListener(this);
@@ -161,6 +160,7 @@
 
     @Override
     public void onAnimationAtStart() {
+        mQuickQsPanel.setVisibility(View.VISIBLE);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/TouchAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/TouchAnimator.java
index 026dd0e..35ade58 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/TouchAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/TouchAnimator.java
@@ -38,7 +38,7 @@
     private final float mSpan;
     private final Interpolator mInterpolator;
     private final Listener mListener;
-    private float mLastT;
+    private float mLastT = -1;
 
     private TouchAnimator(Object[] targets, KeyframeSet[] keyframeSets,
             float startDelay, float endDelay, Interpolator interpolator, Listener listener) {
@@ -56,15 +56,16 @@
         if (mInterpolator != null) {
             t = mInterpolator.getInterpolation(t);
         }
+        if (t == mLastT) {
+            return;
+        }
         if (mListener != null) {
-            if (mLastT == 0 || mLastT == 1) {
-                if (t != mLastT) {
-                    mListener.onAnimationStarted();
-                }
-            } else if (t == 1) {
+            if (t == 1) {
                 mListener.onAnimationAtEnd();
             } else if (t == 0) {
                 mListener.onAnimationAtStart();
+            } else if (mLastT <= 0 || mLastT == 1) {
+                mListener.onAnimationStarted();
             }
             mLastT = t;
         }
@@ -114,12 +115,12 @@
         private Listener mListener;
 
         public Builder addFloat(Object target, String property, float... values) {
-            add(target, KeyframeSet.ofFloat(getProperty(target, property), values));
+            add(target, KeyframeSet.ofFloat(getProperty(target, property, float.class), values));
             return this;
         }
 
         public Builder addInt(Object target, String property, int... values) {
-            add(target, KeyframeSet.ofInt(getProperty(target, property), values));
+            add(target, KeyframeSet.ofInt(getProperty(target, property, int.class), values));
             return this;
         }
 
@@ -128,7 +129,7 @@
             mValues.add(keyframeSet);
         }
 
-        private static Property getProperty(Object target, String property) {
+        private static Property getProperty(Object target, String property, Class<?> cls) {
             if (target instanceof View) {
                 switch (property) {
                     case "translationX":
@@ -151,7 +152,7 @@
                         return View.SCALE_Y;
                 }
             }
-            return Property.of(target.getClass(), float.class, property);
+            return Property.of(target.getClass(), cls, property);
         }
 
         public Builder setStartDelay(float startDelay) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
index 67fe8e5e..225c10f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
@@ -77,11 +77,10 @@
         TypedValue value = new TypedValue();
         mContext.getTheme().resolveAttribute(android.R.attr.homeAsUpIndicator, value, true);
         mToolbar.setNavigationIcon(
-                getResources().getDrawable(R.drawable.ic_close_white, mContext.getTheme()));
+                getResources().getDrawable(value.resourceId, mContext.getTheme()));
         mToolbar.setNavigationOnClickListener(new OnClickListener() {
             @Override
             public void onClick(View v) {
-                save();
                 hide((int) v.getX() + v.getWidth() / 2, (int) v.getY() + v.getHeight() / 2);
             }
         });
@@ -115,6 +114,7 @@
     public void hide(int x, int y) {
         if (isShown) {
             isShown = false;
+            save();
             mClipper.animateCircularClip(x, y, false, mCollapseAnimationListener);
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
index c6c497f..d1953b1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
@@ -27,7 +27,6 @@
 import android.view.LayoutInflater;
 import android.view.MotionEvent;
 import android.view.View;
-import android.view.View.OnTouchListener;
 import android.view.ViewGroup;
 import android.widget.FrameLayout;
 import com.android.systemui.R;
@@ -159,13 +158,6 @@
 
         TileInfo info = mTiles.get(position);
         holder.mTileView.onStateChanged(info.state);
-        holder.mTileView.setOnTouchListener(new OnTouchListener() {
-            @Override
-            public boolean onTouch(View v, MotionEvent event) {
-                mItemTouchHelper.startDrag(holder);
-                return true;
-            }
-        });
     }
 
     public SpanSizeLookup getSizeLookup() {
@@ -179,6 +171,7 @@
             super(itemView);
             if (itemView instanceof FrameLayout) {
                 mTileView = (QSTileView) ((FrameLayout) itemView).getChildAt(0);
+                mTileView.setBackground(null);
             }
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
index e4e3790..12c8c44 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
@@ -69,6 +69,11 @@
     }
 
     @Override
+    public boolean isAvailable() {
+        return mFlashlightController.hasFlashlight();
+    }
+
+    @Override
     protected void handleClick() {
         if (ActivityManager.isUserAMonkey()) {
             return;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvActivity.java b/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvActivity.java
index 9450287..0c48cf7 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvActivity.java
@@ -97,6 +97,9 @@
 
         @Override
         public void onPipResizeAboutToStart() { }
+
+        @Override
+        public void onMediaControllerChanged() { }
     };
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStatusBarHeader.java
index cf5531f..b549d59 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStatusBarHeader.java
@@ -38,7 +38,6 @@
 import com.android.systemui.qs.QSTile;
 import com.android.systemui.qs.QuickQSPanel;
 import com.android.systemui.qs.TouchAnimator;
-import com.android.systemui.qs.TouchAnimator.Listener;
 import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.statusbar.policy.NextAlarmController;
 import com.android.systemui.statusbar.policy.NextAlarmController.NextAlarmChangeCallback;
@@ -46,7 +45,7 @@
 import com.android.systemui.tuner.TunerService;
 
 public class QuickStatusBarHeader extends BaseStatusBarHeader implements
-        NextAlarmChangeCallback, OnClickListener, Listener {
+        NextAlarmChangeCallback, OnClickListener {
 
     private static final String TAG = "QuickStatusBarHeader";
 
@@ -157,7 +156,6 @@
         mAnimator = new TouchAnimator.Builder()
                 .addFloat(mSettingsContainer, "translationY", -mGearTranslation, 0)
                 .addFloat(mMultiUserSwitch, "translationY", -mGearTranslation, 0)
-                .setListener(this)
                 .build();
         mSecondHalfAnimator = new TouchAnimator.Builder()
                 .addFloat(mSettingsButton, "rotation", -180, 0)
@@ -224,20 +222,6 @@
         mExpandIndicator.setExpanded(headerExpansionFraction > EXPAND_INDICATOR_THRESHOLD);
     }
 
-    @Override
-    public void onAnimationAtStart() {
-    }
-
-    @Override
-    public void onAnimationAtEnd() {
-        mHeaderQsPanel.setVisibility(View.INVISIBLE);
-    }
-
-    @Override
-    public void onAnimationStarted() {
-        mHeaderQsPanel.setVisibility(View.VISIBLE);
-    }
-
     private void updateAlarmVisibilities() {
         mAlarmStatus.setVisibility(mAlarmShowing ? View.VISIBLE : View.INVISIBLE);
         mAlarmStatusCollapsed.setVisibility(mAlarmShowing ? View.VISIBLE : View.INVISIBLE);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightController.java
index 29a8f67..9a21a1e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightController.java
@@ -93,6 +93,10 @@
         }
     }
 
+    public boolean hasFlashlight() {
+        return mCameraId != null;
+    }
+
     public synchronized boolean isEnabled() {
         return mFlashlightEnabled;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/tv/pip/PipManager.java b/packages/SystemUI/src/com/android/systemui/tv/pip/PipManager.java
index 0925638..123165e 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/pip/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/pip/PipManager.java
@@ -23,11 +23,14 @@
 import android.app.IActivityManager;
 import android.app.ITaskStackListener;
 import android.content.BroadcastReceiver;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.res.Resources;
 import android.graphics.Rect;
+import android.media.session.MediaController;
+import android.media.session.MediaSessionManager;
 import android.os.Debug;
 import android.os.Handler;
 import android.os.RemoteException;
@@ -69,6 +72,7 @@
 
     private Context mContext;
     private IActivityManager mActivityManager;
+    private MediaSessionManager mMediaSessionManager;
     private int mState = STATE_NO_PIP;
     private final Handler mHandler = new Handler();
     private List<Listener> mListeners = new ArrayList<>();
@@ -79,6 +83,8 @@
     private Rect mRecentsFocusedPipBounds;
     private boolean mInitialized;
     private int mPipTaskId = TASK_ID_NO_PIP;
+    private ComponentName mPipComponentName;
+    private MediaController mPipMediaController;
     private boolean mOnboardingShown;
 
     private boolean mIsRecentsShown;
@@ -100,10 +106,15 @@
             }
             if (DEBUG) Log.d(TAG, "PINNED_STACK:" + stackInfo);
             mPipTaskId = stackInfo.taskIds[stackInfo.taskIds.length - 1];
+            mPipComponentName = ComponentName.unflattenFromString(
+                    stackInfo.taskNames[stackInfo.taskNames.length - 1]);
             // Set state to overlay so we show it when the pinned stack animation ends.
             mState = STATE_PIP_OVERLAY;
             mCurrentPipBounds = mPipBounds;
             launchPipOnboardingActivityIfNeeded();
+            mMediaSessionManager.addOnActiveSessionsChangedListener(
+                    mActiveMediaSessionListener, null);
+            updateMediaController(mMediaSessionManager.getActiveSessions(null));
         }
     };
     private final Runnable mOnTaskStackChanged = new Runnable() {
@@ -176,6 +187,13 @@
 
         }
     };
+    private final MediaSessionManager.OnActiveSessionsChangedListener mActiveMediaSessionListener =
+            new MediaSessionManager.OnActiveSessionsChangedListener() {
+                @Override
+                public void onActiveSessionsChanged(List<MediaController> controllers) {
+                    updateMediaController(controllers);
+                }
+            };
 
     private PipManager() { }
 
@@ -215,6 +233,9 @@
         mContext.registerReceiver(mBroadcastReceiver, intentFilter);
         mOnboardingShown = Prefs.getBoolean(
                 mContext, TV_PICTURE_IN_PICTURE_ONBOARDING_SHOWN, false);
+
+        mMediaSessionManager =
+                (MediaSessionManager) mContext.getSystemService(Context.MEDIA_SESSION_SERVICE);
     }
 
     /**
@@ -248,6 +269,8 @@
     private void closePipInternal(boolean removePipStack) {
         mState = STATE_NO_PIP;
         mPipTaskId = TASK_ID_NO_PIP;
+        mPipMediaController = null;
+        mMediaSessionManager.removeOnActiveSessionsChangedListener(mActiveMediaSessionListener);
         if (removePipStack) {
             try {
                 mActivityManager.removeStack(PINNED_STACK_ID);
@@ -502,6 +525,34 @@
         }
     }
 
+    private void updateMediaController(List<MediaController> controllers) {
+        MediaController mediaController = null;
+        if (controllers != null && mState != STATE_NO_PIP && mPipComponentName != null) {
+            for (int i = controllers.size() - 1; i >= 0; i--) {
+                MediaController controller = controllers.get(i);
+                // We assumes that an app with PIPable activity
+                // keeps the single instance of media controller especially when PIP is on.
+                if (controller.getPackageName().equals(mPipComponentName.getPackageName())) {
+                    mediaController = controller;
+                    break;
+                }
+            }
+        }
+        if (mPipMediaController != mediaController) {
+            mPipMediaController = mediaController;
+            for (int i = mListeners.size() - 1; i >= 0; i--) {
+                mListeners.get(i).onMediaControllerChanged();
+            }
+        }
+    }
+
+    /**
+     * Gets the {@link android.media.session.MediaController} for the PIPed activity.
+     */
+    MediaController getMediaController() {
+        return mPipMediaController;
+    }
+
     private class TaskStackListener extends ITaskStackListener.Stub {
         @Override
         public void onTaskStackChanged() throws RemoteException {
@@ -542,6 +593,8 @@
         void onMoveToFullscreen();
         /** Invoked when we are above to start resizing the Pip. */
         void onPipResizeAboutToStart();
+        /** Invoked when the MediaController on PIPed activity is changed. */
+        void onMediaControllerChanged();
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/tv/pip/PipMenuActivity.java b/packages/SystemUI/src/com/android/systemui/tv/pip/PipMenuActivity.java
index a392bec..fb7fa4d 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/pip/PipMenuActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/pip/PipMenuActivity.java
@@ -18,34 +18,49 @@
 
 import android.app.Activity;
 import android.media.session.MediaController;
+import android.media.session.PlaybackState;
 import android.os.Bundle;
 import android.view.View;
+import android.widget.ImageView;
+import android.widget.TextView;
 
 import com.android.systemui.R;
 
+import static android.content.pm.PackageManager.FEATURE_LEANBACK;
+import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
+import static android.media.session.PlaybackState.ACTION_PAUSE;
+import static android.media.session.PlaybackState.ACTION_PLAY;
+
 /**
  * Activity to show the PIP menu to control PIP.
  */
 public class PipMenuActivity extends Activity implements PipManager.Listener {
     private static final String TAG = "PipMenuActivity";
-    private static final boolean DEBUG = false;
 
     private final PipManager mPipManager = PipManager.getInstance();
     private MediaController mMediaController;
 
     private View mFullButtonView;
     private View mFullDescriptionView;
-    private View mPlayPauseButtonView;
-    private View mPlayPauseDescriptionView;
+    private View mPlayPauseView;
+    private ImageView mPlayPauseButtonImageView;
+    private TextView mPlayPauseDescriptionTextView;
     private View mCloseButtonView;
     private View mCloseDescriptionView;
 
+    private MediaController.Callback mMediaControllerCallback = new MediaController.Callback() {
+        @Override
+        public void onPlaybackStateChanged(PlaybackState state) {
+            updatePlayPauseView(state);
+        }
+    };
+
     @Override
     protected void onCreate(Bundle bundle) {
         super.onCreate(bundle);
         setContentView(R.layout.tv_pip_menu);
         mPipManager.addListener(this);
-        mFullButtonView = findViewById(R.id.full);
+        mFullButtonView = findViewById(R.id.full_button);
         mFullDescriptionView = findViewById(R.id.full_desc);
         mFullButtonView.setOnClickListener(new View.OnClickListener() {
             @Override
@@ -61,22 +76,33 @@
             }
         });
 
-        mPlayPauseButtonView = findViewById(R.id.play_pause);
-        mPlayPauseDescriptionView = findViewById(R.id.play_pause_desc);
-        mPlayPauseButtonView.setOnClickListener(new View.OnClickListener() {
+        mPlayPauseView = findViewById(R.id.play_pause);
+        mPlayPauseButtonImageView = (ImageView) findViewById(R.id.play_pause_button);
+        mPlayPauseDescriptionTextView = (TextView) findViewById(R.id.play_pause_desc);
+        mPlayPauseButtonImageView.setOnClickListener(new View.OnClickListener() {
             @Override
             public void onClick(View v) {
-                // TODO: Implement play/pause.
+                if (mMediaController == null || mMediaController.getPlaybackState() == null) {
+                    return;
+                }
+                long actions = mMediaController.getPlaybackState().getActions();
+                int state = mMediaController.getPlaybackState().getState();
+                if (((actions & ACTION_PLAY) != 0) && !isPlaying(state)) {
+                    mMediaController.getTransportControls().play();
+                } else if ((actions & ACTION_PAUSE) != 0 && isPlaying(state)) {
+                    mMediaController.getTransportControls().pause();
+                }
+                // View will be updated later in {@link mMediaControllerCallback}
             }
         });
-        mPlayPauseButtonView.setOnFocusChangeListener(new View.OnFocusChangeListener() {
+        mPlayPauseButtonImageView.setOnFocusChangeListener(new View.OnFocusChangeListener() {
             @Override
             public void onFocusChange(View v, boolean hasFocus) {
-                mPlayPauseDescriptionView.setVisibility(hasFocus ? View.VISIBLE : View.INVISIBLE);
+                mPlayPauseDescriptionTextView.setVisibility(hasFocus ? View.VISIBLE : View.INVISIBLE);
             }
         });
 
-        mCloseButtonView = findViewById(R.id.close);
+        mCloseButtonView = findViewById(R.id.close_button);
         mCloseDescriptionView = findViewById(R.id.close_desc);
         mCloseButtonView.setOnClickListener(new View.OnClickListener() {
             @Override
@@ -91,6 +117,50 @@
                 mCloseDescriptionView.setVisibility(hasFocus ? View.VISIBLE : View.INVISIBLE);
             }
         });
+        updateMediaController();
+    }
+
+    private void updateMediaController() {
+        MediaController newController = mPipManager.getMediaController();
+        if (mMediaController == newController) {
+            return;
+        }
+        if (mMediaController != null) {
+            mMediaController.unregisterCallback(mMediaControllerCallback);
+        }
+        mMediaController = newController;
+        if (mMediaController != null) {
+            mMediaController.registerCallback(mMediaControllerCallback);
+            updatePlayPauseView(mMediaController.getPlaybackState());
+        } else {
+            updatePlayPauseView(null);
+        }
+    }
+
+    private void updatePlayPauseView(PlaybackState playbackState) {
+        if (playbackState != null
+                && (playbackState.getActions() & (ACTION_PLAY | ACTION_PAUSE)) != 0) {
+            mPlayPauseView.setVisibility(View.VISIBLE);
+            if (isPlaying(playbackState.getState())) {
+                mPlayPauseButtonImageView.setImageResource(R.drawable.tv_pip_pause_button);
+                mPlayPauseDescriptionTextView.setText(R.string.pip_pause);
+            } else {
+                mPlayPauseButtonImageView.setImageResource(R.drawable.tv_pip_play_button);
+                mPlayPauseDescriptionTextView.setText(R.string.pip_play);
+            }
+        } else {
+            mPlayPauseView.setVisibility(View.GONE);
+        }
+    }
+
+    private boolean isPlaying(int state) {
+        return state == PlaybackState.STATE_BUFFERING
+                || state == PlaybackState.STATE_CONNECTING
+                || state == PlaybackState.STATE_PLAYING
+                || state == PlaybackState.STATE_FAST_FORWARDING
+                || state == PlaybackState.STATE_REWINDING
+                || state == PlaybackState.STATE_SKIPPING_TO_PREVIOUS
+                || state == PlaybackState.STATE_SKIPPING_TO_NEXT;
     }
 
     private void restorePipAndFinish() {
@@ -107,6 +177,9 @@
     @Override
     protected void onDestroy() {
         super.onDestroy();
+        if (mMediaController != null) {
+            mMediaController.unregisterCallback(mMediaControllerCallback);
+        }
         mPipManager.removeListener(this);
         mPipManager.resumePipResizing(
                 PipManager.SUSPEND_PIP_RESIZE_REASON_WAITING_FOR_MENU_ACTIVITY_FINISH);
@@ -131,6 +204,11 @@
     }
 
     @Override
+    public void onMediaControllerChanged() {
+        updateMediaController();
+    }
+
+    @Override
     public void onPipResizeAboutToStart() {
         finish();
         mPipManager.suspendPipResizing(
diff --git a/packages/SystemUI/src/com/android/systemui/tv/pip/PipOnboardingActivity.java b/packages/SystemUI/src/com/android/systemui/tv/pip/PipOnboardingActivity.java
index e5c07d2..ad45625b 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/pip/PipOnboardingActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/pip/PipOnboardingActivity.java
@@ -82,6 +82,8 @@
     }
 
     @Override
-    public void onPipResizeAboutToStart() {
-    }
+    public void onPipResizeAboutToStart() { }
+
+    @Override
+    public void onMediaControllerChanged() { }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/tv/pip/PipOverlayActivity.java b/packages/SystemUI/src/com/android/systemui/tv/pip/PipOverlayActivity.java
index cfeab6d..95d655c 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/pip/PipOverlayActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/pip/PipOverlayActivity.java
@@ -103,4 +103,8 @@
         mPipManager.suspendPipResizing(
                 PipManager.SUSPEND_PIP_RESIZE_REASON_WAITING_FOR_OVERLAY_ACTIVITY_FINISH);
     }
+
+    @Override
+    public void onMediaControllerChanged() {
+    }
 }
diff --git a/packages/SystemUI/tests/AndroidManifest.xml b/packages/SystemUI/tests/AndroidManifest.xml
index 2825601..5389c804 100644
--- a/packages/SystemUI/tests/AndroidManifest.xml
+++ b/packages/SystemUI/tests/AndroidManifest.xml
@@ -20,6 +20,8 @@
     <uses-permission android:name="android.permission.INJECT_EVENTS" />
     <uses-permission android:name="android.permission.READ_PHONE_STATE" />
     <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" />
+    <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
+    <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
     <uses-permission android:name="android.permission.MANAGE_USERS" />
 
     <application>
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/TouchAnimatorTests.java b/packages/SystemUI/tests/src/com/android/systemui/qs/TouchAnimatorTests.java
new file mode 100644
index 0000000..1d81fd4
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/TouchAnimatorTests.java
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2016 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.view.View;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.qs.TouchAnimator.Listener;
+import org.mockito.Mockito;
+
+public class TouchAnimatorTests extends SysuiTestCase {
+
+    private Listener mTouchListener;
+    private View mTestView;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        mTestView = new View(getContext());
+        mTouchListener = Mockito.mock(Listener.class);
+    }
+
+    public void testSetValueFloat() {
+        TouchAnimator animator = new TouchAnimator.Builder()
+                .addFloat(mTestView, "x", 0, 50)
+                .build();
+
+        animator.setPosition(0);
+        assertEquals(0f, mTestView.getX());
+
+        animator.setPosition(.5f);
+        assertEquals(25f, mTestView.getX());
+
+        animator.setPosition(1);
+        assertEquals(50f, mTestView.getX());
+    }
+
+    public void testSetValueInt() {
+        TouchAnimator animator = new TouchAnimator.Builder()
+                .addInt(mTestView, "top", 0, 50)
+                .build();
+
+        animator.setPosition(0);
+        assertEquals(0, mTestView.getTop());
+
+        animator.setPosition(.5f);
+        assertEquals(25, mTestView.getTop());
+
+        animator.setPosition(1);
+        assertEquals(50, mTestView.getTop());
+    }
+
+    public void testStartDelay() {
+        TouchAnimator animator = new TouchAnimator.Builder()
+                .addFloat(mTestView, "x", 0, 50)
+                .setStartDelay(.5f)
+                .build();
+
+        animator.setPosition(0);
+        assertEquals(0f, mTestView.getX());
+
+        animator.setPosition(.5f);
+        assertEquals(0f, mTestView.getX());
+
+        animator.setPosition(.75f);
+        assertEquals(25f, mTestView.getX());
+
+        animator.setPosition(1);
+        assertEquals(50f, mTestView.getX());
+    }
+
+    public void testEndDelay() {
+        TouchAnimator animator = new TouchAnimator.Builder()
+                .addFloat(mTestView, "x", 0, 50)
+                .setEndDelay(.5f)
+                .build();
+
+        animator.setPosition(0);
+        assertEquals(0f, mTestView.getX());
+
+        animator.setPosition(.25f);
+        assertEquals(25f, mTestView.getX());
+
+        animator.setPosition(.5f);
+        assertEquals(50f, mTestView.getX());
+
+        animator.setPosition(1);
+        assertEquals(50f, mTestView.getX());
+    }
+
+    public void testOnAnimationAtStartCallback() {
+        TouchAnimator animator = new TouchAnimator.Builder()
+                .setListener(mTouchListener)
+                .build();
+
+        // Called on init.
+        animator.setPosition(0);
+        verifyOnAnimationAtStart(1);
+
+        // Not called from same state.
+        animator.setPosition(0);
+        verifyOnAnimationAtStart(1);
+
+        // Called after starting and moving back to start.
+        animator.setPosition(.5f);
+        animator.setPosition(0);
+        verifyOnAnimationAtStart(2);
+
+        // Called when move from end to end.
+        animator.setPosition(1);
+        animator.setPosition(0);
+        verifyOnAnimationAtStart(3);
+    }
+
+    public void testOnAnimationAtEndCallback() {
+        TouchAnimator animator = new TouchAnimator.Builder()
+                .setListener(mTouchListener)
+                .build();
+
+        // Called on init.
+        animator.setPosition(1);
+        verifyOnAnimationAtEnd(1);
+
+        // Not called from same state.
+        animator.setPosition(1);
+        verifyOnAnimationAtEnd(1);
+
+        // Called after starting and moving back to end.
+        animator.setPosition(.5f);
+        animator.setPosition(1);
+        verifyOnAnimationAtEnd(2);
+
+        // Called when move from end to end.
+        animator.setPosition(0);
+        animator.setPosition(1);
+        verifyOnAnimationAtEnd(3);
+    }
+
+    public void testOnAnimationStartedCallback() {
+        TouchAnimator animator = new TouchAnimator.Builder()
+                .setListener(mTouchListener)
+                .build();
+
+        // Called on init.
+        animator.setPosition(.5f);
+        verifyOnAnimationStarted(1);
+
+        // Not called from same state.
+        animator.setPosition(.6f);
+        verifyOnAnimationStarted(1);
+
+        // Called after going to end then moving again.
+        animator.setPosition(1);
+        animator.setPosition(.5f);
+        verifyOnAnimationStarted(2);
+
+        // Called after moving to start then moving again.
+        animator.setPosition(0);
+        animator.setPosition(.5f);
+        verifyOnAnimationStarted(3);
+    }
+
+    // TODO: Add test for interpolator.
+
+    private void verifyOnAnimationAtStart(int times) {
+        Mockito.verify(mTouchListener, Mockito.times(times)).onAnimationAtStart();
+    }
+
+    private void verifyOnAnimationAtEnd(int times) {
+        Mockito.verify(mTouchListener, Mockito.times(times)).onAnimationAtEnd();
+    }
+
+    private void verifyOnAnimationStarted(int times) {
+        Mockito.verify(mTouchListener, Mockito.times(times)).onAnimationStarted();
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTests.java b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTests.java
index c4ca039..1841251 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTests.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTests.java
@@ -19,6 +19,9 @@
 import android.os.Looper;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.statusbar.phone.QSTileHost;
+import com.android.systemui.statusbar.policy.DataSaverController;
+import com.android.systemui.statusbar.policy.HotspotController;
+import com.android.systemui.statusbar.policy.NetworkController;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mockito;
 
@@ -34,7 +37,12 @@
     protected void setUp() throws Exception {
         super.setUp();
         mManagers = new ArrayList<>();
-        QSTileHost host = new QSTileHost(mContext, null, null, null, null, null, null, null, null,
+        final NetworkController networkController = Mockito.mock(NetworkController.class);
+        Mockito.when(networkController.getDataSaverController()).thenReturn(
+                Mockito.mock(DataSaverController.class));
+        QSTileHost host = new QSTileHost(mContext, null, null, null, null,
+                networkController, null,
+                Mockito.mock(HotspotController.class), null,
                 null, null, null, null, null, null, null);
         mTileService = new TestTileServices(host, Looper.myLooper());
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
index 5cf3767..ebd5384 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
@@ -16,9 +16,6 @@
 
 package com.android.systemui.statusbar.policy;
 
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
 import android.content.Intent;
 import android.net.ConnectivityManager;
 import android.net.NetworkCapabilities;
@@ -31,14 +28,12 @@
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
 import android.util.Log;
-
 import com.android.internal.telephony.cdma.EriInfo;
 import com.android.settingslib.net.DataUsageController;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.statusbar.policy.NetworkController.IconState;
 import com.android.systemui.statusbar.policy.NetworkControllerImpl.Config;
 import com.android.systemui.statusbar.policy.NetworkControllerImpl.SubscriptionDefaults;
-
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mockito;
 
@@ -47,6 +42,9 @@
 import java.util.ArrayList;
 import java.util.List;
 
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
 public class NetworkControllerBaseTest extends SysuiTestCase {
     private static final String TAG = "NetworkControllerBaseTest";
     protected static final int DEFAULT_LEVEL = 2;
@@ -109,6 +107,7 @@
     protected void setupNetworkController() {
         // For now just pretend to be the data sim, so we can test that too.
         mSubId = SubscriptionManager.DEFAULT_SUBSCRIPTION_ID;
+        when(mMockTm.getDataEnabled(mSubId)).thenReturn(true);
         setDefaultSubId(mSubId);
         setSubscriptions(mSubId);
         mMobileSignalController = mNetworkController.mMobileSignalControllers.get(mSubId);
diff --git a/services/core/java/com/android/server/pm/EphemeralApplicationRegistry.java b/services/core/java/com/android/server/pm/EphemeralApplicationRegistry.java
index 8786350..389e0a1 100644
--- a/services/core/java/com/android/server/pm/EphemeralApplicationRegistry.java
+++ b/services/core/java/com/android/server/pm/EphemeralApplicationRegistry.java
@@ -210,6 +210,9 @@
     }
 
     public void onPackageUninstalledLPw(PackageParser.Package pkg) {
+        if (pkg == null) {
+            return;
+        }
         PackageSetting ps = (PackageSetting) pkg.mExtras;
         if (ps == null) {
             return;
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index aa0db99..9988694 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -2449,33 +2449,6 @@
             reconcileAppsData(StorageManager.UUID_PRIVATE_INTERNAL, UserHandle.USER_SYSTEM,
                     storageFlags);
 
-            if (!StorageManager.isFileBasedEncryptionEnabled()
-                    && PackageManager.APPLY_FORCE_DEVICE_ENCRYPTED) {
-                // When upgrading a non-FBE device, we might need to shuffle
-                // around the default storage location of system apps
-                final List<UserInfo> users = sUserManager.getUsers(true);
-                for (PackageSetting ps : mSettings.mPackages.values()) {
-                    if (ps.pkg == null || !ps.isSystem()) continue;
-                    final int storageTarget = ps.pkg.applicationInfo.isForceDeviceEncrypted()
-                            ? StorageManager.FLAG_STORAGE_DE : StorageManager.FLAG_STORAGE_CE;
-                    for (UserInfo user : users) {
-                        if (ps.getInstalled(user.id)) {
-                            try {
-                                mInstaller.migrateAppData(StorageManager.UUID_PRIVATE_INTERNAL,
-                                        ps.name, user.id, storageTarget);
-                            } catch (InstallerException e) {
-                                logCriticalInfo(Log.WARN,
-                                        "Failed to migrate " + ps.name + ": " + e.getMessage());
-                            }
-                            // We may have just shuffled around app data
-                            // directories, so prepare it one more time
-                            prepareAppData(StorageManager.UUID_PRIVATE_INTERNAL, user.id,
-                                    storageFlags, ps.pkg, false);
-                        }
-                    }
-                }
-            }
-
             // If this is first boot after an OTA, and a normal boot, then
             // we need to clear code cache directories.
             if (mIsUpgrade && !onlyCore) {
@@ -13330,18 +13303,21 @@
         return null;
     }
 
-    private void removeNativeBinariesLI(PackageParser.Package pkg) {
+    private void removeNativeBinariesLI(PackageSetting ps) {
         // Remove the lib path for the parent package
-        PackageSetting ps = (PackageSetting) pkg.mExtras;
         if (ps != null) {
             NativeLibraryHelper.removeNativeBinariesLI(ps.legacyNativeLibraryPathString);
-        }
-        // Remove the lib path for the child packages
-        final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
-        for (int i = 0; i < childCount; i++) {
-            ps = (PackageSetting) pkg.childPackages.get(i).mExtras;
-            if (ps != null) {
-                NativeLibraryHelper.removeNativeBinariesLI(ps.legacyNativeLibraryPathString);
+            // Remove the lib path for the child packages
+            final int childCount = (ps.childPackageNames != null) ? ps.childPackageNames.size() : 0;
+            for (int i = 0; i < childCount; i++) {
+                PackageSetting childPs = null;
+                synchronized (mPackages) {
+                    childPs = mSettings.peekPackageLPr(ps.childPackageNames.get(i));
+                }
+                if (childPs != null) {
+                    NativeLibraryHelper.removeNativeBinariesLI(childPs
+                            .legacyNativeLibraryPathString);
+                }
             }
         }
     }
@@ -14464,7 +14440,7 @@
     private boolean deleteSystemPackageLI(PackageParser.Package deletedPkg,
             PackageSetting deletedPs, int[] allUserHandles, int flags, PackageRemovedInfo outInfo,
             boolean writeSettings) {
-        if (deletedPkg.parentPackage != null) {
+        if (deletedPs.parentPackageName != null) {
             Slog.w(TAG, "Attempt to delete child system package " + deletedPkg.packageName);
             return false;
         }
@@ -14477,7 +14453,7 @@
         // the system pkg from system partition
         // reader
         synchronized (mPackages) {
-            disabledPs = mSettings.getDisabledSystemPkgLPr(deletedPkg.packageName);
+            disabledPs = mSettings.getDisabledSystemPkgLPr(deletedPs.name);
         }
 
         if (DEBUG_REMOVE) Slog.d(TAG, "deleteSystemPackageLI: newPs=" + deletedPkg.packageName
@@ -14503,10 +14479,10 @@
         // Delete the updated package
         outInfo.isRemovedPackageSystemUpdate = true;
         if (outInfo.removedChildPackages != null) {
-            final int childCount = (deletedPkg.childPackages != null)
-                    ? deletedPkg.childPackages.size() : 0;
+            final int childCount = (deletedPs.childPackageNames != null)
+                    ? deletedPs.childPackageNames.size() : 0;
             for (int i = 0; i < childCount; i++) {
-                String childPackageName = deletedPkg.childPackages.get(i).packageName;
+                String childPackageName = deletedPs.childPackageNames.get(i);
                 if (disabledPs.childPackageNames != null && disabledPs.childPackageNames
                         .contains(childPackageName)) {
                     PackageRemovedInfo childInfo = outInfo.removedChildPackages.get(
@@ -14526,7 +14502,7 @@
             flags |= PackageManager.DELETE_KEEP_DATA;
         }
 
-        boolean ret = deleteInstalledPackageLI(deletedPkg, true, flags, allUserHandles,
+        boolean ret = deleteInstalledPackageLI(deletedPs, true, flags, allUserHandles,
                 outInfo, writeSettings, disabledPs.pkg);
         if (!ret) {
             return false;
@@ -14537,7 +14513,7 @@
             // Reinstate the old system package
             enableSystemPackageLPw(disabledPs.pkg);
             // Remove any native libraries from the upgraded package.
-            removeNativeBinariesLI(deletedPkg);
+            removeNativeBinariesLI(deletedPs);
         }
 
         // Install the system package
@@ -14594,29 +14570,18 @@
         return true;
     }
 
-    private boolean deleteInstalledPackageLI(PackageParser.Package pkg,
+    private boolean deleteInstalledPackageLI(PackageSetting ps,
             boolean deleteCodeAndResources, int flags, int[] allUserHandles,
             PackageRemovedInfo outInfo, boolean writeSettings,
             PackageParser.Package replacingPackage) {
-        PackageSetting ps = null;
-
         synchronized (mPackages) {
-            pkg = mPackages.get(pkg.packageName);
-            if (pkg == null) {
-                return false;
-            }
-
-            ps = mSettings.mPackages.get(pkg.packageName);
-            if (ps == null) {
-                return false;
-            }
-
             if (outInfo != null) {
                 outInfo.uid = ps.appId;
             }
 
             if (outInfo != null && outInfo.removedChildPackages != null) {
-                final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
+                final int childCount = (ps.childPackageNames != null)
+                        ? ps.childPackageNames.size() : 0;
                 for (int i = 0; i < childCount; i++) {
                     String childPackageName = ps.childPackageNames.get(i);
                     PackageSetting childPs = mSettings.mPackages.get(childPackageName);
@@ -14636,11 +14601,11 @@
         removePackageDataLI(ps, allUserHandles, outInfo, flags, writeSettings);
 
         // Delete the child packages data
-        final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
+        final int childCount = (ps.childPackageNames != null) ? ps.childPackageNames.size() : 0;
         for (int i = 0; i < childCount; i++) {
             PackageSetting childPs;
             synchronized (mPackages) {
-                childPs = mSettings.peekPackageLPr(pkg.childPackages.get(i).packageName);
+                childPs = mSettings.peekPackageLPr(ps.childPackageNames.get(i));
             }
             if (childPs != null) {
                 PackageRemovedInfo childOutInfo = (outInfo != null
@@ -14656,7 +14621,7 @@
         }
 
         // Delete application code and resources only for parent packages
-        if (ps.pkg.parentPackage == null) {
+        if (ps.parentPackageName == null) {
             if (deleteCodeAndResources && (outInfo != null)) {
                 outInfo.args = createInstallArgsForExisting(packageFlagsToInstallFlags(ps),
                         ps.codePathString, ps.resourcePathString, getAppDexInstructionSets(ps));
@@ -14747,7 +14712,7 @@
                 return false;
             }
 
-            if (ps.pkg != null && ps.pkg.parentPackage != null && (!isSystemApp(ps)
+            if (ps.parentPackageName != null && (!isSystemApp(ps)
                     || (flags & PackageManager.DELETE_SYSTEM_APP) != 0)) {
                 if (DEBUG_REMOVE) {
                     Slog.d(TAG, "Uninstalled child package:" + packageName + " for user:"
@@ -14835,7 +14800,7 @@
             if (DEBUG_REMOVE) Slog.d(TAG, "Removing non-system package: " + ps.name);
             // Kill application pre-emptively especially for apps on sd.
             killApplication(packageName, ps.appId, "uninstall pkg");
-            ret = deleteInstalledPackageLI(ps.pkg, deleteCodeAndResources, flags, allUserHandles,
+            ret = deleteInstalledPackageLI(ps, deleteCodeAndResources, flags, allUserHandles,
                     outInfo, writeSettings, replacingPackage);
         }
 
@@ -18021,6 +17986,13 @@
 
             if (ps.getInstalled(userId)) {
                 prepareAppData(volumeUuid, userId, flags, ps.pkg, restoreconNeeded);
+
+                if (maybeMigrateAppData(volumeUuid, userId, ps.pkg)) {
+                    // We may have just shuffled around app data directories, so
+                    // prepare them one more time
+                    prepareAppData(volumeUuid, userId, flags, ps.pkg, restoreconNeeded);
+                }
+
                 preparedCount++;
             }
         }
@@ -18148,6 +18120,30 @@
         }
     }
 
+    /**
+     * For system apps on non-FBE devices, this method migrates any existing
+     * CE/DE data to match the {@code forceDeviceEncrypted} flag requested by
+     * the app.
+     */
+    private boolean maybeMigrateAppData(String volumeUuid, int userId, PackageParser.Package pkg) {
+        if (pkg.isSystemApp() && !StorageManager.isFileBasedEncryptionEnabled()
+                && PackageManager.APPLY_FORCE_DEVICE_ENCRYPTED) {
+            final int storageTarget = pkg.applicationInfo.isForceDeviceEncrypted()
+                    ? StorageManager.FLAG_STORAGE_DE : StorageManager.FLAG_STORAGE_CE;
+            synchronized (mInstallLock) {
+                try {
+                    mInstaller.migrateAppData(volumeUuid, pkg.packageName, userId, storageTarget);
+                } catch (InstallerException e) {
+                    logCriticalInfo(Log.WARN,
+                            "Failed to migrate " + pkg.packageName + ": " + e.getMessage());
+                }
+            }
+            return true;
+        } else {
+            return false;
+        }
+    }
+
     private void unfreezePackage(String packageName) {
         synchronized (mPackages) {
             final PackageSetting ps = mSettings.mPackages.get(packageName);
diff --git a/telecomm/java/android/telecom/Conference.java b/telecomm/java/android/telecom/Conference.java
index 094b3a9..1b70d65 100644
--- a/telecomm/java/android/telecom/Conference.java
+++ b/telecomm/java/android/telecom/Conference.java
@@ -181,7 +181,10 @@
      * @hide
      */
     public void removeCapability(int capability) {
-        mConnectionCapabilities &= ~capability;
+        int newCapabilities = mConnectionCapabilities;
+        newCapabilities &= ~capability;
+
+        setConnectionCapabilities(newCapabilities);
     }
 
     /**
@@ -191,7 +194,10 @@
      * @hide
      */
     public void addCapability(int capability) {
-        mConnectionCapabilities |= capability;
+        int newCapabilities = mConnectionCapabilities;
+        newCapabilities |= capability;
+
+        setConnectionCapabilities(newCapabilities);
     }
 
     /**
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index 9f478df..857d2df 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -1426,6 +1426,7 @@
      * {@link android.provider.BlockedNumberContract#canCurrentUserBlockNumbers(Context)} returns
      * {@code true} for the current user.
      */
+    // TODO: Delete this.
     public void launchManageBlockedNumbersActivity() {
         ITelecomService service = getTelecomService();
         if (service != null) {
@@ -1437,6 +1438,26 @@
         }
     }
 
+    /**
+     * Creates the {@link Intent} which can be used with {@link Context#startActivity(Intent)} to
+     * launch the activity to manage blocked numbers.
+     * <p> This method displays the UI to manage blocked numbers only if
+     * {@link android.provider.BlockedNumberContract#canCurrentUserBlockNumbers(Context)} returns
+     * {@code true} for the current user.
+     */
+    public Intent createManageBlockedNumbersIntent() {
+        ITelecomService service = getTelecomService();
+        Intent result = null;
+        if (service != null) {
+            try {
+                result = service.createManageBlockedNumbersIntent();
+            } catch (RemoteException e) {
+                Log.e(TAG, "Error calling ITelecomService#createManageBlockedNumbersIntent", e);
+            }
+        }
+        return result;
+    }
+
     private ITelecomService getTelecomService() {
         if (mTelecomServiceOverride != null) {
             return mTelecomServiceOverride;
diff --git a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
index 95c8db5..3c250f1 100644
--- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
+++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
@@ -17,6 +17,7 @@
 package com.android.internal.telecom;
 
 import android.content.ComponentName;
+import android.content.Intent;
 import android.telecom.ParcelableCallAnalytics;
 import android.telecom.PhoneAccountHandle;
 import android.net.Uri;
@@ -247,5 +248,11 @@
     /**
     * @see TelecomServiceImpl#launchManageBlockedNumbersActivity
     **/
+    // TODO: Delete this.
     void launchManageBlockedNumbersActivity(in String callingPackageName);
+
+    /**
+    * @see TelecomServiceImpl#createManageBlockedNumbersIntent
+    **/
+    Intent createManageBlockedNumbersIntent();
 }
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/RefactorClassAdapter.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/RefactorClassAdapter.java
index 91161f5..024e32f 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/RefactorClassAdapter.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/RefactorClassAdapter.java
@@ -16,9 +16,11 @@
 
 package com.android.tools.layoutlib.create;
 
+import java.util.Arrays;
 import java.util.HashMap;
 
 import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.MethodVisitor;
 
 public class RefactorClassAdapter extends AbstractClassAdapter {
 
@@ -30,6 +32,14 @@
     }
 
     @Override
+    public MethodVisitor visitMethod(int access, String name, String desc, String signature,
+            String[] exceptions) {
+        MethodVisitor mw = super.visitMethod(access, name, desc, signature, exceptions);
+
+        return new RefactorStackMapAdapter(mw);
+    }
+
+    @Override
     protected String renameInternalType(String oldClassName) {
         if (oldClassName != null) {
             String newName = mRefactorClasses.get(oldClassName);
@@ -46,4 +56,49 @@
         }
         return oldClassName;
     }
+
+    /**
+     * A method visitor that renames all references from an old class name to a new class name in
+     * the stackmap of the method.
+     */
+    private class RefactorStackMapAdapter extends MethodVisitor {
+
+        private RefactorStackMapAdapter(MethodVisitor mv) {
+            super(Main.ASM_VERSION, mv);
+        }
+
+
+        private Object[] renameFrame(Object[] elements) {
+            if (elements == null) {
+                return null;
+            }
+
+            // The input array cannot be modified. We only copy the source array on write
+            boolean copied = false;
+            for (int i = 0; i < elements.length; i++) {
+                if (!(elements[i] instanceof String)) {
+                    continue;
+                }
+
+                if (!copied) {
+                    elements = Arrays.copyOf(elements, elements.length);
+                    copied = true;
+                }
+
+                String type = (String)elements[i];
+                if (type.indexOf(';') > 0) {
+                    elements[i] = renameTypeDesc(type);
+                } else {
+                    elements[i] = renameInternalType(type);
+                }
+            }
+
+            return elements;
+        }
+
+        @Override
+        public void visitFrame(int type, int nLocal, Object[] local, int nStack, Object[] stack) {
+            super.visitFrame(type, nLocal, renameFrame(local), nStack, renameFrame(stack));
+        }
+    }
 }